In-depth analysis of task switching and stack by Liu Wanli:
Http://www.linuxforum.net/forum/showflat.php? Cat = & board = linuxk & number = 301272 & fpart = all
Keywords: time interruption, task switching, stack, linux0.01
Introduction:
What is the relationship between Task Switching and stack? Many friends may not know the relationship between them, and some may think that there is not much relationship between them (document 4 ). In my opinion, task switching is closely related to the stack! The following is my discussion on the relationship between them. Here, the task switching refers to the task switching that occurs during forced scheduling when the time is interrupted, so I started to discuss the interruption when considering the stack below. Of course, when I conduct this analysis, I also feel the complexity of them and the mistakes are inevitable. I hope you can correct them more. Recommended reader level :***
I. Time interruption.
Assume that a process is executed in the user space (CPL = 3 at this time) and the time is interrupted. The interrupt processing process is (document 1: p438 ):
1. Locate the interrupt gate descriptor Based on the interrupt vector number;
2. Extract and select Sub-, offset, and attribute fields from the descriptor and perform corresponding privilege checks;
3. Transfer to the corresponding interrupt handler for execution based on the descriptor type.
Is it too superficial? Take a look (document 1: p439, figure 10.20 ):
1. Is the selection blank? No. continue;
2. obtain the corresponding descriptor. (The DPL attribute in the descriptor should be 0. 3. interrupt vector initialization part)
3. What is the storage segment descriptor? Yes continue;
4. What is the DPL <CPL and the DPL segment exists? Yes continue; according to the assumption CPL = 3, DPL = 0, so to 5!
5. Switch to the inner stack!
How to switch ?? Because a process has a user space stack and a system space (also called the kernel space) stack, no matter where the user space stack is, it should be specified by ss2 in the task status segment of the process, ss0 specifies the system space stack, which occupies a page space with the Process Task Structure task_struct (see Article 3: sched. c ). Therefore, the switch to the inner stack should assign the ss0 value in the TSS of the process to the SS register.
6. Make RPL = 0;
7. Load the descriptor into CS;
8. Is the portal offset out of bounds? No. continue;
9. eflag, Cs, and EIP are added to the stack!
10. TF = 0, NT = 0, and if = 0. Here, the interrupt door is considered.
11. Transfer to handling procedures.
Don't worry. Let's first look at the current stack situation:
| Outer EIP |
| External CS |
| Eflag |
| Outer ESP |
| Outer layer SS |
-----------
Where is this stack located? This is very important! This was done when switching to the inner stack, that is, the system space stack of the current process, that is, the stack that occupies a page with task_struct. What is saved here is the stack and code information of the process in the user space, so that the process can be resumed after the interruption is completed.
2. Interrupt the processing program.
This refers to the time interruption. (Document 3: system_call.c: timer_interrupt :)
Timer_interrupt:
1. Push % DS
2. Push % es
3. Push % FS
4. pushl % edX
5. pushl % ECx
6. pushl % EBX
7. pushl % eax
8. movl $0x10, % eax
9. mov % ax, % DS
10. mov % ax, % es
11. movl $0x17, % eax
12. mov % ax, % FS
13. incl jiffies
14. movb $0x20, % Al
15. outb % Al, $0x20
16. movl CS (% ESP), % eax
17. Andl $3, % eax
18. pushl % eax
19. Call do_timer
20. Andl $4, % ESP
21. JMP ret_from_sys_call
From 1 to 7 behavior-based stack operation, this is what we care about! 16-18 is to apply CPL (CPL = cs & 3) to the stack for the do_tiemr (long CPL) function. So what about the stack when it is executed into do_timer? Let's see:
| Return address |
-----------
| CPL |
| Eax |
| EBX |
| ECx |
| EdX |
| FS |
| Es |
| DS |
-----------
| Outer EIP |
| External CS |
| Eflag |
| Outer ESP |
| Outer layer SS |
-----------
The return address above is of course the statement after do_timer is called, that is, 20 rows of andl $4, % ESP statements. So is it because schedule () is called in the do_timer () function and a task switchover occurs! Ah, it's so troublesome. I don't know when to return it here. Let's take a look at it step by step.
3. do_timer () (document 3: sched. C: do_timer ())
Void do_timer (long CPL)
{
...
If (-- Current-> counter)> 0) return;
Current-> counter = 0;
If (! CPL) return;
Schedule ();
}
The ellipsis is irrelevant to the two statements for process timing. If the time slice is not used up (counter> 0) or the CPL is 0, the system will return directly without scheduling. Of course, it will not directly return to the previously executed process space, but will return to do_timer (), pay attention to the starting return address, and then return from the interrupt processing to the process through the iret command. Of course, according to our assumptions, CPL should be 3 here, because it is interrupted in the user space. We will discuss this issue from the most complex situations. Now, let's go to the center. Please enter schedule ().
4. Schedule (). (Document 3: sched. C: Schedule ())
Void schedule (void)
{
Int next;
...
Switch_to (next );
}
Oh, here I have omitted a few more codes. It executes the scheduling algorithm, that is, to find the next process to be executed from all the processes in the 'run' state, then, assign the number to next. Switch!
Switch_to () is a macro defined in (document 3: sched. h:
# Define switch_to (n ){/
Struct (long a, B;} _ TMP ;/
_ ASM _ ("CMPL % ECx, current/n/t "/
"Je 1f/n/t "/
"Xchgl % ECx, current/n/t "/
"Movw % dx, % 1/N/t "/
"Ljmp * % 0/n/t "/
"CMPL % ECx, % 2/n/t "/
"JNE 1f/n/t "/
"Clts/N "/
"1 :"/
: "M" (* & __ TMP. A), "M" (* & __ TMP. B ),/
"M" (last_task_used_math), "D" _ TSS (N), "C" (long) task [N]);/
}
This is the key code for Task Switching. The principle is to switch tasks directly through TSS (document 1: p420 ). Let me explain this key code line by line. CMPL % ECx, current, compare whether Task n is the current process. If yes, you do not need to switch it, and stop schedule () directly (). Xchgl % ECx, current, the current Pointer Points to the task structure of task N, and the ECX register saves the task structure pointer of the current process. Movw % dx, % 1, make _ TMP. B = 'tss select sub' for the n tasks in gdt. Note that _ TSS (n) is the macro for selecting sub-tasks! Ljmp * % 0. This code is a real task switchover. The at&t syntax ljmp is equivalent to Intel's JMP far section: the offset command format, and its absolute address is preceded. Here we reference (Article 1: p420) A Passage: When the Selection Sub-pointer contained in the Inter-segment Transfer Instruction JMP instructs a available task status segment TSS descriptor, under normal circumstances, switching from the current task to the available task occurs. The entry point of the target task is determined by the pointer specified by the Cs and EIP fields in the TSS of the target task. The offset in the JMP command is discarded. You may have to flip the specific task switchover (Article 1: p421). Here I will only talk about stack processing, that is, to save the Register to the TSS of the current task. Save the current values of General registers, segment registers, EIPs, and eflags to the current TSS. The value of the saved EIP is the return address, pointing to the next instruction that causes the task switching command; restoring the register field of the target task, recover all common registers, segment registers, eflag, and EIPs Based on the content stored in the TSS. Well, there are so many references to the basic concepts, so the process mentioned above will be switched out immediately. What is it to save the EIP in TSS? Obviously, according to the analysis just now, the command CMPL % ECx, % 2 should be used. What does this mean? That is to say, if the next time this task is to be switched to the running state, it will start to run from the command CMPL % ECx, % 2! Then, the task is pushed from another to this task. That is to say, when we switch to the next task, it is also executed from this command! So we enter the stack space of the next task and start execution, however, because the next task has the same stack path as the current task (is this the same as the kernel control path in Linux ?), Therefore, we still reference the current stack to continue the analysis.
Oh, a little confused, it seems like. Take a break and refer to (document 2: p373 ). The same is true for experts ;)
Otherwise, we understand that the interrupted process has been forcibly scheduled, and task switching has also occurred, but it is switched to itself, not actually. Okay, JMP is successful and execution starts.
5. The turning point is returned from Schedule.
CMPL % ECx, % 2; JNE 1f; clts; 1: these statements are related to the coprocessor and the TS mark. Let's go straight to 1, start to return from Schedule (). Note that switch_to () is a macro, which is at the end of Schedule. Where did I go? Follow up and look at the stack above. The return address is the statement after do_timer is called,
Addl $4, % ESP
JMP ret_from_sys_call
Here ESP plus 4 is to remove CPL from the stack, because we don't need to jump to ret_from_sys_call. Oh, the remaining processing and System Call return share the code.
6. ret_from_sys_call (document 3, kernel/system_call.s)
Let's take a look at our focus. What about the stack?
| Eax |
| EBX |
| ECx |
| EdX |
| FS |
| Es |
| DS |
-----------
| Outer EIP |
| External CS |
| Eflag |
| Outer ESP |
| Outer layer SS |
-----------
Ret_from_sys_call:
Movel current, % eax
CMPL task, % eax
Je 3f
Movl CS (% ESP), % EBX
Testl $3, % EBX
Je 3f
Cmpw $0x17, oldss (% ESP)
JNE 3f
2:
....
3:
Popl % eax
Popl % EBX
Popl % ECx
Popl % edX
Pop % FS
Pop % es
Pop % DS
Iret
I omitted some related signals and other processing at the 2 mark. Let's analyze, if the current task is a process no. 0, or the previous CPL of the task is 3 (that is, the user State), or the previous stack segment of the task is the stack specified in the LDT, JMP to 3. The previous assumptions show that the CPL of this task is 3. Restores the eax, EBC, ECx, EDX, FS, es, and DS registers from the stack.
The current stack is as follows:
| Outer EIP |
| External CS |
| Eflag |
| Outer ESP |
| Outer layer SS |
-----------
Remember the last statement, iret. This command is familiar to everyone. It restores EIP, Cs, eflag, ESP, and SS. Do you remember that this has recovered to the moment when the process was interrupted at the initial time? Congratulations! You can finally continue what you need to do! Be careful, there will be another interruption, oh, you are not afraid? Because it will not affect your coherence.