2. Scheduler work timing
There are usually two ways to start the scheduler:
A. Active
Call Schedule directly in the core application. This usually occurs when the process needs to be placed in a suspend (sleep) state because of the waiting core event - then actively request schedule to facilitate other processes to use the CPU. Below is an example of active schedule:
/ * Extract from [Drivers / INPUT / MOUSEDEV.C] mousedev_read () * /
Add_wait_Queue (& List-> Mousedev-> Wait, & Wait);
Current-> State = Task_Interruptible;
While (! List-> Ready) {
IF (file-> f_flags & o_nonblock) {
Retval = -eagain;
Break;
}
IF (Signal_pending (current)) {
RETVAL = -Restartsys;
Break;
}
Schedule ();
}
Current-> state = task_running; / * This sentence can actually omit, because the status of the process has been restored to task_running during the wakeup process * /
REMOVE_WAIT_QUEUE (& List-> Mousedev-> Wait, & Wait);
The process can usually be divided into four steps:
Add the process to the event waiting queue;
Set the process status is Task_Interruptible (or Task_uninterruptible);
Check whether the wait condition is satisfied in the loop, and it is not satisfied, and schedule () is called, and the loop will be exited.
Remove the process from the event waiting queue.
From the Distributor Workflow, we know that the scheduler will delete the process in the sleep state from the ready queue, and only the process in the ready queue may be scheduled. The action replaced in the ready queue is done during the "wake-up" process when the event occurs. In the mouse drive shown above, the mouse interrupt will call the mousedev_event () function, which will use Wake_up_interruptible () to wake up all the processes waiting for the mouse event. Wake_up_interruptible () will eventually call the try_to_wake_up () function:
/ * Extreme [kernel / SCHED.C] * /
Static Inline INT TRY_TO_WAKE_UP (Struct Task_struct * p, int synchronous)
{
Unsigned long flag;
INT Success = 0;
Spin_lock_irqsave (& Runqueue_lock, Flags);
P-> State = Task_running;
IF (Task_on_runqueue (p))
Goto Out;
Add_to_runqueue (p); / * Add to the ready queue * /
IF (! Synchronous ||! (P-> CPUS_ALLOWED & (1 << SMP_PROCESSOR_ID ())))))))))
RESCHEDULE_IDLE (P); / * Call wake_up () in this case, Synchronous is always 0, at this time, * /
/ * If this CPU is not suitable for running the process, you need to call Reschadule_Idle () to find the right CPU * /
Success = 1;
OUT:
Spin_unlock_irqrestore (& Runqueue_lock, Flags);
Return Success;
At this time, SCHEDULE () is passive.
B. Passive
After the system call execution is completed, the control is returned by the core state to the user state, and Linux will check the NEED_RESCHED value of the current process at the RET_FROM_SYS_CALL entry, if the value is 1, then call schedule ():
/ * Infaite [Arch / I386 / kernel / entry.s] * /
Entry (RET_FROM_SYS_CALL)
CLI
CMPL $ 0, NEED_RESCHED (% EBX) #ebx stores a Current pointer
Jne Reschandule
......
Reschedule:
Call symbol_name (Schedule)
JMP RET_FROM_SYS_CALL # repeatedly query NEED_RESCHED
Therefore, you only need to set up the current process NEED_RESCHED, there is a chance to start the scheduler. There is usually a few occasions will set up_resched:
Update_process_times (), triggered by the clock interrupt, responsible for managing the time slice consumption of other processes other than the 0th process (IDLE process). If the current process (except for the SCHED_FIFO real-time process) is used up (counter == 0), set NEED_RESCHED to 1; (Note: At this time, it does not calculate or reset the counter value, this work is in all processes. After completing, in Schedule () in Schedule ())
ResChedule_idle (), this function has been described in detail in the "Scheduler Workflow" section, but the most frequent caller is the wakeup process of the process of sleep on a group waiting in the queue --wake_up_process () and Other series of WAKE_UP functions (see "active scheduling");
SCHED_SETSCHEDULER (), SCHED_YIELD () system call, and system initialization (REST_INIT ()), create a new process (in do_fork ()), etc., the like, want to start the scheduler work.
Since the timing of starting the schedule () is actually determined by the current process, it is not meant to schedule it in time, which is also the cause of "Linux kernel can't be sew" (see "Some of Linux 2.4 scheduling system" "Nuclear can't be preemptive").