Linux wake-up preemption----management and scheduling of Linux processes (23)

Source: Internet
Author: User

Date Kernel version Architecture author GitHub CSDN
2016-07-05 Linux-4.6 X86 & Arm Gatieme Linuxdevicedrivers Linux process management and scheduling

CFS is responsible for handling common non-real-time processes, which are the most common processes in our Linux

1 Foreground review 1.1 CFS scheduling algorithm

The idea of CFS scheduling algorithm

Ideally, each process can have the same time slice and run on the CPU simultaneously, but actually one CPU can only have one process running at the same time. That is, when a process consumes the CPU, other processes must wait. CFS in order to achieve fairness, the currently running process must be punished so that those waiting processes are scheduled the next time.

1.2 Today's theme-wake-up preemption

When the process is awakened in try_to_wake_up/wake_up_process and Wake_up_new_task, the kernel uses the global Check_preempt_curr to see if a process can preempt the current process to preempt the currently running process. Note that the process does not involve the core scheduler.

Each scheduler class should implement a Check_preempt_curr function, in which the Scheduler class Check_preempt_curr which the process belongs to is called in global Check_preempt_curr for preemption checking. The policy is executed by the Check_preempt_wakeup function for the process of the fully fair scheduler CFS process.

The newly-awakened process does not have to be handled by the fully-fair scheduler, and if the new process is a real-time process, the dispatch is immediately requested because the real-time process takes precedence and the real-time process always grabs the CFS process.

2 Sleep of the Linux process

In Linux, processes that wait only for CPU time are called ready processes, they are placed in a running queue, and the status flag bit for a ready process is task_running. Once a running process time slice is exhausted, the Linux kernel scheduler will deprive the process of control of the CPU and choose a suitable process from the running queue to run.

Of course, a process can also voluntarily release control of the CPU. The function schedule () is a dispatch function that can be invoked by a process to schedule other processes to occupy the CPU. Once the process that actively abandons the CPU is re-dispatched to occupy the CPU, it executes from the location where it was last stopped, that is, it will start at the next line of code called Schedule ().

Sometimes processes need to wait until a particular event occurs, such as device initialization, completion of I/O operations, or timer-to-time. In this case, the process must be moved out of the running queue and joined to a wait queue, when the process goes to sleep.

There are two kinds of process sleep states in Linux

    • One is an interruptible sleep state, with its status flag bit task_interruptible.

      A process with an interrupted sleep state sleeps until a condition becomes true, such as creating a hardware interrupt, releasing the system resources that the process is waiting for, or passing a signal that can be the condition of the wake process.

    • The other is a non-interruptible sleep state with a status flag of task_uninterruptible.

      The non-disruptive sleep state is similar to an interruptible sleep state, but it has one exception: the process of transmitting a signal to this sleep state cannot change its state, meaning it does not respond to the wake of the signal. Non-disruptive sleep states are generally less useful, but in some specific situations this state can be helpful, for example: a process must wait and not be interrupted until a particular event occurs.

In modern Linux operating systems, the process is generally used to call the schedule method to sleep, the following code shows how to let the running process go to sleep state.

sleeping_task = current;set_current_state(TASK_INTERRUPTIBLE);schedule();func1();/* Rest of the code ... */

In the first statement, the program stores a copy of the process structure pointer, Sleeping_task, which is a macro that points to the process structure being executed. Set_current_state () Changes the state of the process from the execution state task_running to the sleep state task_interruptible.

    • If schedule is scheduled by a process with a state of task_running, then schedule will dispatch another process to occupy the CPU;

    • If schedule is scheduled by a process with a state of task_interruptible or task_uninterruptible, then an additional step will be executed: the currently executing process will be removed from the run queue before another process is dispatched. This causes the process that is running to go to sleep because it is no longer in the running queue.

3 Wake on Linux process

When the process is awakened in try_to_wake_up/wake_up_process and Wake_up_new_task, the kernel uses the global Check_preempt_curr to see if a process can preempt the current process to preempt the currently running process. Note that the process does not involve the core scheduler.

3.1 wake_up_process

We can use Wake_up_process to wake up the process that went into sleep, the function is defined in kernel/sched/core.c, line 2043.

int wake_up_process(struct task_struct *p){    return0);}

After Wake_up_process is called, the state of the sleep process is set to task_running, and the scheduler adds it to the run queue. Of course, this process can only be really put into operation the next time the scheduler is dispatched.

3.2 try_to_wake_up

The TRY_TO_WAKE_UP function achieves the purpose of waking the sleep and stopping process by setting the process state to task_running and inserting the process into the local CPU run queue RQ.

For example, call this function to wake a process in a waiting queue, or resume a process that performs a wait signal.

staticinttry_to_wake_up(structunsignedintint wake_flags)

The function accepts the parameters: the descriptor pointer of the awakened process (p), the process state mask that can be awakened (the states), a flag wake_flags, which prevents the awakened process from seizing the running process on the local CPU.

try_to_wake_up function definition in kernel/sched/core.c, line 1906

3.3 Wake_up_new_task
void wake_up_new_task(struct task_struct *p)

The function is defined in [Kernel/sched/core.c, line 2421
] (http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.6#L2421
)

Before going to sleep can wake up through try_to_wake_up and wake_up_process, and we fork the newly created process can complete the wake-up work by Wake_up_new_task after completing its own creation. See analysis of process creation process under Linux (_do_fork/do_fork) –linux process management and scheduling (eight)

When using fork to create a process, the kernel invokes the _do_fork (early kernel counterpart do_fork) function to complete the creation of the kernel, where the process information is created and can be used to wake the process and add it to the ready queue by using Wake_up_new_task. Code See KERNEL/FORK.C, line 1755

3.4 Check_preempt_curr

When the process is awakened in Wake_up_new_task, the kernel uses the global Check_preempt_curr to see if a process can preempt the current process to preempt the currently running process.

    check_preempt_curr(rq, p, WF_FORK);

function definition in kernel/sched/core.c, line 905

voidCheck_preempt_curr (structRQ *rq,structTask_struct *p,intFlags) {Const structSched_class *class;if(P->sched_class = = Rq->curr->sched_class)    {Rq->curr->sched_class->check_preempt_curr (RQ, p, flags); }Else{For_each_class (class) {if(class= = Rq->curr->sched_class) Break;if(class= = P->sched_class) {Resched_curr (RQ); Break; }        }    }/ * * A queue event has occurred, and we ' re going to schedule.     In * This case, we can save a useless back to the back clock update. */    if(task_on_rq_queued (Rq->curr) && test_tsk_need_resched (Rq->curr)) Rq_clock_skip_update (RQ,true);}
4 Invalid Wakeup 4.1 concept of invalid Wakeup

In almost all cases, the process will go to sleep after checking for certain conditions and discovering that the condition is not satisfied. But sometimes the process will start to sleep when the condition is true, and if so the process will hibernate indefinitely, this is called the invalid wake-up problem.

In the operating system, when multiple processes attempt to do some processing of the shared data, and the final result depends on the order in which the process is run, a race condition occurs, which is a typical problem in the operating system, which is caused by an invalid wake-up due to a competitive condition.

Imagine that there are two processes A and B, a process is processing a linked list, it needs to check whether the list is empty, if not empty the data inside the list to do some operations, and B process is also in the list to add nodes. When the list is empty, because of countless data can be manipulated, then a process goes to sleep, when the B process adds a node to the list, it wakes up a process, its code is as follows:

A process:

spin_lock(&list_lock);if(list_empty(&list_head)){    spin_unlock(&list_lock);    set_current_state(TASK_INTERRUPTIBLE);    schedule();    spin_lock(&list_lock);}/* Rest of the code ... */spin_unlock(&list_lock);}

B Process:

spin_lock(&list_lock);list_add_tail(&list_head, new_node);spin_unlock(&list_lock);wake_up_process(A);

A problem arises if the B process is scheduled to run by another processor when the a process executes until the 5th line after line 4th. In this time slice, the B process executes all of its instructions, so it tries to wake up the a process, and the a process does not go to sleep, so the wake operation is not valid.

After that, the a process continues to execute, and it mistakenly considers the list to be empty at this time, and then sets its state to task_interruptible and then calls schedule () into sleep. Because Miss B process wakes up, it will sleep indefinitely, this is the invalid wake problem, because even if there is data in the list to be processed, a process is still sleep.

4.2 Reason for invalid wakeup

How do you avoid invalid wake-up problems?

We found that the invalid wakeup occurred mainly after the check condition and the process state was set to sleep before the wake_up_process of the B process provided an opportunity to set the a process state to task_running, but this time the status of a process is still task_ RUNNING, so Wake_up_process's effort to shift the state of a process from sleep to running state has not worked as expected.

4.3 Avoiding invalid preemption

To solve this problem, it is necessary to use a safeguard mechanism to make it an inseparable step to judge the list to be empty and to set the state of the process to sleep, that is, the root cause of the competition condition must be eliminated, so that the Wake_up_ The process can act as a wake-up state that is a part of the sleep state.

Once you have found the reason, redesign the code structure of the a process to avoid the invalid wake-up problem in the example above.

A process

set_current_state(TASK_INTERRUPTIBLE);spin_lock(&list_lock);if(list_empty(&list_head)){    spin_unlock(&list_lock);    schedule();    ... */spin_unlock(&list_lock);

As you can see, this code sets the current execution process state to task_interruptible before testing the condition, and resets itself to the task_running state if the linked list is not empty.

In this way if the B process in the A process check the list is empty after calling Wake_up_process, then the status of a process will automatically change from the original task_interruptible to task_running, and even if the process calls schedule, Because it is now task_running, it is still not removed from the run queue, so it does not go to sleep incorrectly, and of course avoids the problem of invalid wakeup.

5 example of the Linux kernel 5.1 A most basic example

In the Linux operating system, kernel stability is critical, in order to avoid invalid wake-up problems in the Linux operating system kernel, the Linux kernel should use the following actions when it requires process sleep:

/* ‘q’是我们希望睡眠的等待队列 */DECLARE_WAITQUEUE(wait,current);add_wait_queue(q, &wait);set_current_state(TASK_INTERRUPTIBLE);/* 或TASK_INTERRUPTIBLE */while/* ‘condition’ 是等待的条件*/schedule();set_current_state(TASK_RUNNING);remove_wait_queue(q, &wait);

The above action allows the process to safely add itself to a waiting queue for sleep by following a series of steps: First call Declare_waitqueue to create an item waiting for the queue, and then call Add_wait_queue () to add itself to the wait queue. and set the status of the process to task_interruptible or task_interruptible.

Then the loop checks whether the condition is true: if so, there is no need to sleep, if the condition is not true, call schedule

When the conditions for the process check are met, the process then sets itself to task_running and calls Remove_wait_queue to move itself out of the waiting queue.

As can be seen from the above, the kernel code maintainer of Linux also sets the state of the process to sleep before the process checks the condition.
The check condition is then cycled. If the condition is reached before the process starts to sleep, the loop exits and sets its state to ready with set_current_state, which also guarantees that the process will not have the wrong tendency to go to sleep and, of course, will not cause an invalid wakeup problem.

There are many places in the kernel that use to avoid invalid wakeup, the most common place is the kernel thread, because the kernel thread's main function is to assist the kernel to do certain work, most of the time they are in sleep state, when the kernel found a task to do, it will wake them.

Example of process number 2nd-Avoid invalid preemption

Let's take a look at the Linux kernel example to see how the Linux kernel avoids invalid sleep, I remember process 2nd, its main task is to inherit the kernel thread Kthread creation, its workflow function is Kthreadd

Code in KERNEL/KTHREAD.C, Kthreadd function, line L514

 for(;;) {set_current_state (task_interruptible);if(List_empty (&kthread_create_list)) schedule ();    __set_current_state (task_running); Spin_lock (&kthread_create_lock);/ * ==do_something start== * /     while(!list_empty (&kthread_create_list)) {structKthread_create_info *create; Create = List_entry (Kthread_create_list.next,structKthread_create_info,List); List_del_init (&create->List);        Spin_unlock (&kthread_create_lock); Create_kthread (create);/ * ==do_something end = = *Spin_lock (&kthread_create_lock); } spin_unlock (&kthread_create_lock);
5.2 Kthread_worker_fn

Kthread_worker/kthread_work is a better way to manage kernel work, and multiple kernel threads can work on the same worker and work together, a bit like the way the thread pool works.

The kernel provides the KTHREAD_WORKER_FN function to run as the THREADFN parameter of the kthread_create or Kthread_run function, which can attach multiple kernel threads to the same worker, The same worker structure will be passed to Kthread_run or kthread_create as THREADFN parameters.

Its KTHREAD_WORKER_FN function acts as the main function frame of the worker, and also contains code to avoid invalid wake-up, KERNEL/KTHREAD.C, kthread_worker_fn functions, line573, as shown below

int kthread_worker_fn(void *worker_ptr){    /* ......*/    set_current_state(TASK_INTERRUPTIBLE);  /* mb paired w/ kthread_stop */    if (kthread_should_stop()) {        __set_current_state(TASK_RUNNING);        spin_lock_irq(&worker->lock);    worker->task = NULL;    spin_unlock_irq(&worker->lock);    return0;    }    /* ......*/}

In addition, the kernel's __kthread_parkme function also contains similar code

6 Summary

From the discussion above, it can be found that the key to avoiding invalid wake-up processes in Linux is

    • Set the status of the process to task_interruptible or task_uninterruptible before the process checks the condition

    • And if the checked condition is met, it should reset its state to task_running.

This avoids an invalid wake-up problem, regardless of whether the process waits for the condition to be satisfied, and the process does not go to sleep incorrectly because it is moved out of the ready queue.

set_current_state(TASK_INTERRUPTIBLE);spin_lock(&list_lock);if(list_empty(&list_head)){    spin_unlock(&list_lock);    schedule();    ... */spin_unlock(&list_lock);

Linux wake-up preemption----management and scheduling of Linux processes (23)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.