four. Process switching process
Switch from a process of context to another process because of its high frequency, usually the key to the efficiency of scheduler. In Linux, this feature is implemented in a classic assembly code, which focuses on this code.
This code segment named Switch_to () is called during the Schedule (), with a macro:
/ * Announcement from [include / asm-i386 / system.h] * /
#define switch_to (prev, next, last) do {/
ASM Volatile ("Pushl %% ESI / N / T" /
"Pushl %% EDI / N / T" /
"Pushl %% EBP / N / T" / save ESI, EDI, EBP register
"MOVL %% ESP,% 0 / N / T" / ESP Save to Prev-> Thread.esp
"MOVL% 3, %% ESP / N / T" / from Next-> Thread.esp Restore ESP
"MOVL $
Allf
% 1 / N / T "/ Save" 1: "jump address in Prev-> thread.eip, will start executing from there when the Prev is switched again
"Pushl% 4 / N / T" / Save next-> thread.eip on the stack, __ switch_to () will go there to perform, that is, enter the context of the next process
"JMP __Switch_to / N" / Jump to __Switch_to (), further processing (see below)
"1: / t" /
"POPL %% EBP / N / T" /
"POPL %% EDI / N / T" /
"POPL %% ESI / N / T" / First resume the register value saved last time, and then returns from Switch_TO ().
: "= m" (prev-> thread.esp), /% 0
"= m" (prev-> thread.eip), /% 1
"= B" / EBX, because the process is switched, the prev information on the recovered stack is not just switched, so it is passed using the EBX register to deliver the value to prev
: "M" (Next-> Thread.esp), /% 3
"M" (Next-> Thread.eip), /% 4
"a" (prev), "D" (Next), / EAX, EDX
"B" (prev)); / EBX
} W
hi
Le (0)
The process switching process can be divided into two phases. The above assembly code can be seen as the first phase, which saves some key registers and sets the address of the new process on the stack. The second phase is started in switch_to (), implemented in the __switch_to () function, mainly for saving and updating some registers (and IO Operation Permissions Map IOPERM): unlazy_fpu (), if old The process sets a PF_USEDFPU bit in the flags of Task_struct, indicating that it uses the FPU, unlazy_fpu () saves the FPU content in Task_Struct :: Thread;
Update ESP0 in the init_TSS in the init_TSS with the new process;
Save the current FS and GS registers in the Task_Struct :: Thread of the old process, then recover the FS and GS registers from the task_struct :: thread of the new process;
Recover the value of six debug registers from the task_struct :: thread of the new process;
Use the IOPERM update of IOPERM in IOPERM in init_TSS
The switch_to () function returns to the back address on the stack is the task_struct :: thread :: EIP of the new process, that is, the new process is started at the last time that the continued operation of the setup (last executed Switch_TO () is launched " 1: "Location). It is running to the context of the new process.
In the previous Linux kernel, the process of switching is FAR JMP instruction, 2.4 uses the hand-control jump as shown above, the action and the time used are similar to FAR JMP, but more to optimize and control.
Fives. Scheduler: The call relationship of the function of the function is implemented, and the basic functions of each function will be described.
Linux's scheduler is mainly implemented in the Schedule () function.
1. Scheduler workflow
The basic process of the Schedule () function can be summarized as four steps:
1). Clean up the process in the current operation
2). Select the next process of putting the operation
3) Set the operating environment of the new process
4). Execute process context switch
5).
It contains some lock operations: Ready queue lock Runquque_lock, global core lock kernel_flag, global interrupt lock global_irq_lock, process list lock tasklist_lock. The following is first describing the working process of the scheduler from the lock operation.
A. Related locks
Runqueue_lock, defined as self-spinch, must be locked before the ready queue is operated;
Kernel_flag is defined as a self-spinker because many core operations (such as driven) need to ensure that only one process is executed only, so you need to call the locked_kernel () / release_kernel () to operate the core lock, it is locked / unlocked kernel_flag Also set the flag on Task_Struct :: Lock_Depth, Lock_Depth is less than 0 means unlocked. When a process switching occurs, the process that is not allowed to be switched with a kernel_flag lock, so it must be called Release_kernel_lock () to force release, and if the new process is put into runtime if Lock_Depth> 0, that is, the process is held before The core lock must be called again to locate again;
GLOBAL_IRQ_LOCK, is defined as a global memory length, using the Clear_bit () / set_bit () series, which is operated with global_irq_holder indicates which CPU has a global interrupt lock, which is suspended in the global interrupt process (see IRQ_Enter ()); TaskList_lock, defined as a read-write lock, protecting the process list structure with init_task.