It seems that Linux 2.4 kernel doesn't save some general register such as EAX,EBX,ECX,etc. during the task switch,
does that mean all the general rigisters can be shared between different task? Please refer to the following codes:
#define switch_to(prev,next,last) do { asm volatile("pushl %%esi\n\t"
"pushl %%edi\n\t"
"pushl %%ebp\n\t" \
"movl %%esp,%0\n\t"/* save ESP */ "movl %3,%%esp\n\t"/* restore ESP */
"movl $1f,%1\n\t" /* save EIP */
"pushl %4\n\t" /* restore EIP */
"jmp __switch_to\n"
"1:\t"
"popl %%ebp\n\t"
"popl %%edi\n\t"
"popl %%esi\n\t"
:"=m" (prev->thread.esp),"=m" (prev->thread.eip),
"=b" (last)
:"m" (next->thread.esp),"m" (next->thread.eip),
"a" (prev), "d" (next),
"b" (prev));
} while (0)
Some confusions about task switch in Linux 2.4
Re:Some confusions about task switch in Linux 2.4
Of course not (but you know that, or you wouldn't ask). That'd mean that you can't rely on any of those registers between a task switch.cxsnew wrote: It seems that Linux 2.4 kernel doesn't save some general register such as EAX,EBX,ECX,etc. during the task switch,
does that mean all the general rigisters can be shared between different task? Please refer to the following codes:
#define switch_to(prev,next,last) do { asm volatile("pushl %%esi\n\t"
"pushl %%edi\n\t"
"pushl %%ebp\n\t" \
"movl %%esp,%0\n\t"/* save ESP */ "movl %3,%%esp\n\t"/* restore ESP */
"movl $1f,%1\n\t" /* save EIP */
"pushl %4\n\t" /* restore EIP */
"jmp __switch_to\n"
"1:\t"
"popl %%ebp\n\t"
"popl %%edi\n\t"
"popl %%esi\n\t"
:"=m" (prev->thread.esp),"=m" (prev->thread.eip),
"=b" (last)
:"m" (next->thread.esp),"m" (next->thread.eip),
"a" (prev), "d" (next),
"b" (prev));
} while (0)
The point of this code is to be called from within an interrupt handler at a time when you want a switch. The user level context is saved by the interrupt handler, while this stuff only saves what the function before it (inside the kernel) needs. The function is a macro define defining an assembly statement with a constraint list at the end, so it doesn't have to save them explicitly (GCC does that for it). I don't know what's going on with ecx, but my guess is that it's not a callee-register so that you'd have to save it anyway. The only part I can't see is how the jump exactly works, but I think it's a C function call. The 1: should be removable without harm (there's nothing jumping to it as far as I can see).
Re:Some confusions about task switch in Linux 2.4
Let me see whether I have understand what you said, you mean for the swith_to MACRO definition, gcc will save those register it used. But except those registers, you know, a lot of registers should be saved as the prev process' context, such as EFLAG,etc. or else these registers will be corrupted by the next process. Althrough schedule() usually called in the system call or interrupt context, but sometimes it will called directly by other processes.
So I am still confusion about how to save the prev process' context during task switch in Linux 2.4 kernel.
So I am still confusion about how to save the prev process' context during task switch in Linux 2.4 kernel.
Re:Some confusions about task switch in Linux 2.4
The registers that are to be saved are:cxsnew wrote: Let me see whether I have understand what you said, you mean for the swith_to MACRO definition, gcc will save those register it used. But except those registers, you know, a lot of registers should be saved as the prev process' context, such as EFLAG,etc. or else these registers will be corrupted by the next process. Althrough schedule() usually called in the system call or interrupt context, but sometimes it will called directly by other processes.
So I am still confusion about how to save the prev process' context during task switch in Linux 2.4 kernel.
- In case of an interrupt, all. The interrupt handler dumps them on the stack, so when the OS interrupt returns (which it does when the OS switches back) it'll be popped back off the stack. All that needs to be stored is what the OS itself needs.
- In case of a call to the OS, only a few. It saves those. When you call the OS you save all variables for the shared registers (eax etc.) and you expect those in nonshared registers to be the same. The OS saves these.
PS: don't forget that Linux uses an interrupt for kernel calls, so in a way they all pass through some form of interrupt handler. I am not aware of what linux does in specific, but my guess is that the IRQ handler saves all and the int80 handler saves only those it shouldn't wreck.
In the paradoxal case that it ended up in the called-method without calling it explicitly it'd be in trouble. Since this is the only combination that would wreck it, and since this combination is paradoxal, the methods cover the problem. The relevant registers are always on the stack in some form or another.
Re:Some confusions about task switch in Linux 2.4
In case of an interrupt:- In case of an interrupt, all. The interrupt handler dumps them on the stack, so when the OS interrupt returns (which it does when the OS switches back) it'll be popped back off the stack. All that needs to be stored is what the OS itself needs.
- In case of a call to the OS, only a few. It saves those. When you call the OS you save all variables for the shared registers (eax etc.) and you expect those in nonshared registers to be the same. The OS saves these.
SAVE_ALL -> schedule() ->RESTORE_ALL
So the general register and EFLAG will be shared between all the codes locate in the middle of SAVE_ALL and RESTORE_ALL, then how can you make sure the EFLAG belong to the Prev process will not changed by the Next process when execute the schedule function? You know, the schedule function will not be executed completely for the Prev process, but for the next time, the Prev process get CPU, it will contine execute from the rest code of the shedule.
In case of a call to the OS, you mean the invoker of the schedule() will do something like SAVE_ALL?
Re:Some confusions about task switch in Linux 2.4
The eflags between the SAVE_ALL and RESTORE_ALL is not of interest. Eflags is just about always changed by any assembly code, GCC knows not to rely on it. The schedule function in the actual switch part is in assembly, with enough guards for GCC to not rely on any other things than the actual information that is usable after that.cxsnew wrote: In case of an interrupt:
SAVE_ALL -> schedule() ->RESTORE_ALL
So the general register and EFLAG will be shared between all the codes locate in the middle of SAVE_ALL and RESTORE_ALL, then how can you make sure the EFLAG belong to the Prev process will not changed by the Next process when execute the schedule function? You know, the schedule function will not be executed completely for the Prev process, but for the next time, the Prev process get CPU, it will contine execute from the rest code of the shedule.
Something like it. See also the ABI for X86 Linux, which explains what should be saved and what may be wrecked upon a function call. On an interrupt, all should be saved. On a schedule() only the things that should be saved have to be saved.In case of a call to the OS, you mean the invoker of the schedule() will do something like SAVE_ALL?
Re:Some confusions about task switch in Linux 2.4
It should be noted that your task switching code is generally called by some other code, frequently in a PIT or RTC interrupt handler. Several registers that do need to be saved will have already been saved by the interrupt handler code. Thus, the Linux task switching code you've quoted has no need to save registers that have already been saved by the interrupt code, so naturally, it doesn't.
If it's not being called from an interrupt but from some function, there's already an agreed upon set of registers ("caller saved" registers) that no function needs to preserve (if the caller cares about their value, it saves them before calling any function), only "callee saved" registers need to be saved.
So, either way, it's highly unlikely that the segment of code that actually does a task switch will need to save or restore more than a couple of registers. If this was not the case, there would be no advantage to using software task switching instead of hardware task switching -- software task switching is faster precisely because it can avoid saving a bunch of registers that it knows don't need to be saved here (since the work has already been done elsewhere).
If it's not being called from an interrupt but from some function, there's already an agreed upon set of registers ("caller saved" registers) that no function needs to preserve (if the caller cares about their value, it saves them before calling any function), only "callee saved" registers need to be saved.
So, either way, it's highly unlikely that the segment of code that actually does a task switch will need to save or restore more than a couple of registers. If this was not the case, there would be no advantage to using software task switching instead of hardware task switching -- software task switching is faster precisely because it can avoid saving a bunch of registers that it knows don't need to be saved here (since the work has already been done elsewhere).