Compiled switch_to
# Define switch_to (prev, next, last )/
Do {/
/*/
* Context-switching clobbers (thoroughly defeated) all registers, So we clobber/
* Them explicitly, via unused output variables ./
* (Eax and EBP is not listed because EBP is saved/restored/
* Explicitly for wchan access and eax is the return value/
* _ Switch_to ())/
*//
Unsigned long EBX, ECx, EDX, ESI, EDI ;/
/
ASM volatile ("pushfl/n/t"/* save flags *//
"Pushl % EBP/n/t"/* save EBP *//
"Movl % ESP, % [prev_sp]/n/t"/* save ESP *//
"Movl % [next_sp], % ESP/n/t"/* restore ESP *//
"Movl $ 1f, % [prev_ip]/n/t"/* save EIP *//
"Pushl % [next_ip]/n/t"/* restore EIP *//
"JMP _ switch_to/N"/* regparm call *//
"1:/t "/
"Popl % EBP/n/t"/* restore EBP *//
"Popfl/N"/* restore flags *//
/
/* Output parameters *//
: [Prev_sp] "= m" (prev-> thread. SP ),/
/* M indicates putting the variable into the memory, that is, putting the variable stored in [prev_sp] into the memory, and then writing the variable into Prev-> thread. sp *//
[Prev_ip] "= m" (prev-> thread. IP ),/
"= A" (last ),/
/* = Indicates the output, and a indicates to put the last variable into ax, eax = last *//
/
/* Clobbered output registers :*//
"= B" (EBX), "= C" (ECx), "= D" (EDX ),/
/* Put the B variable into EBX, and C to put ECx, d to edX, s to put Si, and D to put EDI *//
"= S" (Esi), "= D" (EDI )/
/
/* Input parameters :*//
: [Next_sp] "M" (next-> thread. SP ),/
/* Next-> put thread. SP into the memory [next_sp] * //
[Next_ip] "M" (next-> thread. IP ),/
/
/* Regparm parameters for _ switch_to ():*//
[Prev] "a" (prev ),/
/* Eax = Prev edX = next *//
[Next] "D" (next )/
/
:/* Reloaded segment REGISTERS *//
"Memory ");/
} While (0)
Standard assembly version switch_to:
1. Store Prev and next in registers, that is, register parameters.
Movl Prev, % eax
Movl next, % edX
2. Store eflags and EBP in the current stack.
Pushfl
Pushl % EBP
3. Save the ESP content to Prev-> thread. ESP to point this field to the top of the prev Kernel stack.
Movl % ESP, 484 (% eax)
Note: 484 (% eax) indicates the memory address = (% eax) + 484
4. Load next-> thread. SP into ESP, and the kernel starts to operate in the next instruction space. This instruction completes switching between processes.
You can think of the thread_info data structure, an 8 k Data Structure Consisting of the kernel stack and process descriptor.
Movl 484 (% EDX), % ESP
5. Save the address marked as 1f to Prev-> thread. EIP. That is, the replaced process starts to execute this command the next time it is selected to be executed by Schedule ().
Movl $ 1f, 480 (% eax)
6. Press the value of next-> thread. EIP (usually an address marked as 1) into the next Kernel stack.
Pushl 480 (% EDX)
7 jump to _ swtich_to () C language function to start execution
JMP _ swtich_to
8 here, the process Prev replaced by the Process next gets the CPU again: it executes some register content commands to save eflags and EBP, the first command of these two commands is marked as 1 (as described in "a deep understanding of Unix ). When _ swtich_to is returned, the IP address has already pointed to the first command of next, because when executing the RET command, the next-> ip address saved in the SP is popped up and assigned to the next EIP. Therefore, it is said that it is incorrect for Prev to obtain the CPU again.
1:
Pop % EBP
Popfl
Starting from 1: it has entered the next instruction space. It can be understood as follows: 1: The first instruction of the next process, which is executed
Pop % EBP
Popfl
Then, the final switch is completed, that is, the stack and status word are restored to the next stack base address and status word.
My personal understanding: the first command of every process in Linux (except the newly created process) should be
1:
Pop % EBP
Popfl
Each process executes these two commands at the beginning before doing their own work.