CPU 寄存器和多任务处理
CPU Registers and Multitasking
我目前正在学习汇编,但我对 CPU 寄存器如何与多任务处理配合使用感到困惑。所以在多任务系统中。 CPU可以随时暂停某个程序的执行,运行另一个程序。那么在这一步中寄存器值是如何保存的呢?寄存器是压入堆栈还是以任何其他方式?
how the CPU registers work with Multitasking.
CPU 基本上可能不适用于多任务处理,任务切换可能(并且已经)在软件中实现。某些 CPU (intel x86) 可能有硬件状态 (TSS https://en.wikipedia.org/wiki/Task_state_segment) and TR (task register) to atomically change state from one task to another. (TSS may still be used to switch protection rings ring0/ring3;但不会切换任务。)
So in a Multitasking System, CPU can pause the execution of a certain program at any time and run another program.
差不多。
在大多数CPU中,能够运行宁OS和用户-space任务,有interrupts which are used in case of some external event (signal from hardware, interrupt request, IRQ) to pause execution of current code (task) and to jump to one of several special kernel functions, called interrupt handlers(ISR,中断服务程序) .
So how are the register values preserved during this step ?
大多数寄存器在进入中断时保持不变。有的在中断进入过程中被CPU保留。
中断支持机制在CPU内部实现,它确实将当前任务的一些 cpu个寄存器保存到一些space(是的,它 can save to stack, x86 cpu pushes *FLAGS, CS, IP). CPU jumps to these routines, registered in Interrupt Vector Table / Interrupt Descriptor Table (IDT) 数组(在 IVT 的情况下存储在特殊的内存位置或由特殊的 CPU 寄存器 IDT 指向 IDT);对于 IRQ 1,它将 select 记录编号 1(例程1); 对于 IRQ 20,它将 select 例程 20.
Are the registers pushed to stack or any other way ?
两者都有。有的是中断中CPU推送的,有的是中断处理程序的命令推送的。 (而且还有在内核态修改EIP的代码...)
ISR routine知道CPU保存了什么,如果它想多用一些寄存器(基本上是要),它会压栈(或者保存到其他地方)其他用户寄存器,然后做它的中断工作,然后恢复手动保存的寄存器,然后使用特殊命令退出中断(iret
in x86 word,它将从堆栈重新加载"CS:IP and FLAGS")。如果堆栈中原始 IP 未被触及,CPU 将继续执行原始用户代码(原始任务),并且所有寄存器都将保持不变。如果作者或中断处理程序想要,他可能会在执行 iret
之前更改堆栈中的 IP 地址,因此它将 return 在其他地方(它可能 return 到内核模式或某些任务或重启 PC 等)。
正在更改当前 运行ning 任务(上下文切换,wiki, osdev) is one of problems which may be done using interrupts. There are basically two kinds of context switching: involuntary (when the task just runs and do not want to leave CPU) and voluntary (when task asks OS to do the context switch, either because current task may no run further or if current task is polite and gives other tasks chance to run - sched_yield 在 linux 世界中)。
非自愿的上下文切换通常是在周期性定时器中断的帮助下完成的。几年前,这个定时器用于每 10、1 或 3 毫秒生成一些特定的 IRQ(timer/RTC IRQ);和定时器中断处理程序调用 OS scheduler to decide is current task runs for too much time (exceeds its time slice, the quantum of time-sharing) 或者是否有一些具有更高优先级的任务准备好 运行.
自愿上下文切换通常是在系统调用的帮助下完成的(当OS使用特权分离时,运行s user-space ring3和kernel-[=112=中的代码] ring0 中的代码 - protection rings), which is special CPU instruction to switch between privilege levels (it may be / was implemented with software-generated interrupts; int 80h
in older linux/BSD). User-space code asks kernel to do some work, for example read
from file or socket or write
to file. It also may ask kernel to run scheduler to switch to other task if there is any - with sched_yield syscall. If syscall code decides that request need some time and the task can't run further before the request is done (system call blocks - blocks current task from running; switches it state from TASK_RUNNING
的概念),它还会调用 OS 调度程序。
OS 调度程序可能会将当前任务保持为 运行ning(如果它处于 运行nable 状态)或者可能决定切换到其他任务(实际上是内核模式其他任务;还有 return-from-syscall 来恢复任务的 CS:IP+FLAGS) 并使用 switch_to
asm 宏来完成:http://lxr.free-electrons.com/source/arch/x86/include/asm/switch_to.h?v=4.6#L27:
33 /* \
34 * Context-switching clobbers all registers, so we clobber \
35 * them explicitly, via unused output variables. \
36 * (EAX and EBP is not listed because EBP is saved/restored \
37 * explicitly for wchan access and EAX is the return value of \
38 * __switch_to()) \
39 */ \
40 unsigned long ebx, ecx, edx, esi, edi; \
41 \
42 asm volatile("pushfl\n\t" /* save flags */ \
43 "pushl %%ebp\n\t" /* save EBP */ \
44 "movl %%esp,%[prev_sp]\n\t" /* save ESP */ \
45 "movl %[next_sp],%%esp\n\t" /* restore ESP */ \
46 "movl f,%[prev_ip]\n\t" /* save EIP */ \
47 "pushl %[next_ip]\n\t" /* restore EIP */ \
48 __switch_canary \
49 "jmp __switch_to\n" /* regparm call */ \
50 "1:\t" \
51 "popl %%ebp\n\t" /* restore EBP */ \
52 "popfl\n" /* restore flags */ \
53 \
如果只有一个非睡眠用户任务进入睡眠状态,则没有可见任务准备就绪 运行。但实际上有一个 pid 为 0 的不可见任务,有时称为 swapper 或 idle, which has lowest priority and always ready to run. It will run some special CPU instruction in loop to cool down the CPU - HLT;它还可以对事件/调用调度程序进行一些检查以查找 运行nable 任务。
一些奇怪的实时 OS(名称从 "V" 开始并以版本 5 结尾)没有 user-space/kernel-space 分离和隔离(所有代码 运行 都在环 0 中) 可以在没有系统调用或软件中断的情况下实现自愿上下文切换;但通常调用调度程序。
有用的链接:
- https://en.wikibooks.org/wiki/X86_Assembly/Advanced_Interrupts
- http://wiki.osdev.org/Interrupt
- http://wiki.osdev.org/Context_Switching
- http://www.informit.com/articles/article.aspx?p=364068 - How Multitasking Works at the Hardware Level, 2005, 非常好的书的章节 "Unabridged Pentium 4, The: IA32 Processor Genealogy".
- Task management on x86 提到 "Protected Mode Software Architecture" 书和 "Understanding the Linux Kernel, 3rd Ed.",Bovet,第 3 章过程,3.3.2。任务状态段。
- http://wiki.osdev.org/Task_State_Segment硬件任务切换
CPU can pause the execution of a certain program at any time and run another program. So how are the register values preserved during this step ? Are the registers pushed to stack or any other way ?
大多数处理器都定义了进程上下文块 (PCB) 结构,用于在暂停执行时保存进程寄存器。这样的处理器还有一个保存过程内容和加载过程上下文指令,可以一步保存或加载寄存器集。
寄存器没有压入堆栈。
进程切换类似于:
SAVEPROCESSCONTEXT address_of_running_process_pcb
LOADPROCESSCONTEXT address_of_new_process_pdb
在大多数系统中,中断由当时 运行 的任何进程提供服务,因此不会暂停进程。
我目前正在学习汇编,但我对 CPU 寄存器如何与多任务处理配合使用感到困惑。所以在多任务系统中。 CPU可以随时暂停某个程序的执行,运行另一个程序。那么在这一步中寄存器值是如何保存的呢?寄存器是压入堆栈还是以任何其他方式?
how the CPU registers work with Multitasking.
CPU 基本上可能不适用于多任务处理,任务切换可能(并且已经)在软件中实现。某些 CPU (intel x86) 可能有硬件状态 (TSS https://en.wikipedia.org/wiki/Task_state_segment) and TR (task register) to atomically change state from one task to another. (TSS may still be used to switch protection rings ring0/ring3;但不会切换任务。)
So in a Multitasking System, CPU can pause the execution of a certain program at any time and run another program.
差不多。
在大多数CPU中,能够运行宁OS和用户-space任务,有interrupts which are used in case of some external event (signal from hardware, interrupt request, IRQ) to pause execution of current code (task) and to jump to one of several special kernel functions, called interrupt handlers(ISR,中断服务程序) .
So how are the register values preserved during this step ?
大多数寄存器在进入中断时保持不变。有的在中断进入过程中被CPU保留。
中断支持机制在CPU内部实现,它确实将当前任务的一些 cpu个寄存器保存到一些space(是的,它 can save to stack, x86 cpu pushes *FLAGS, CS, IP). CPU jumps to these routines, registered in Interrupt Vector Table / Interrupt Descriptor Table (IDT) 数组(在 IVT 的情况下存储在特殊的内存位置或由特殊的 CPU 寄存器 IDT 指向 IDT);对于 IRQ 1,它将 select 记录编号 1(例程1); 对于 IRQ 20,它将 select 例程 20.
Are the registers pushed to stack or any other way ?
两者都有。有的是中断中CPU推送的,有的是中断处理程序的命令推送的。 (而且还有在内核态修改EIP的代码...)
ISR routine知道CPU保存了什么,如果它想多用一些寄存器(基本上是要),它会压栈(或者保存到其他地方)其他用户寄存器,然后做它的中断工作,然后恢复手动保存的寄存器,然后使用特殊命令退出中断(iret
in x86 word,它将从堆栈重新加载"CS:IP and FLAGS")。如果堆栈中原始 IP 未被触及,CPU 将继续执行原始用户代码(原始任务),并且所有寄存器都将保持不变。如果作者或中断处理程序想要,他可能会在执行 iret
之前更改堆栈中的 IP 地址,因此它将 return 在其他地方(它可能 return 到内核模式或某些任务或重启 PC 等)。
正在更改当前 运行ning 任务(上下文切换,wiki, osdev) is one of problems which may be done using interrupts. There are basically two kinds of context switching: involuntary (when the task just runs and do not want to leave CPU) and voluntary (when task asks OS to do the context switch, either because current task may no run further or if current task is polite and gives other tasks chance to run - sched_yield 在 linux 世界中)。
非自愿的上下文切换通常是在周期性定时器中断的帮助下完成的。几年前,这个定时器用于每 10、1 或 3 毫秒生成一些特定的 IRQ(timer/RTC IRQ);和定时器中断处理程序调用 OS scheduler to decide is current task runs for too much time (exceeds its time slice, the quantum of time-sharing) 或者是否有一些具有更高优先级的任务准备好 运行.
自愿上下文切换通常是在系统调用的帮助下完成的(当OS使用特权分离时,运行s user-space ring3和kernel-[=112=中的代码] ring0 中的代码 - protection rings), which is special CPU instruction to switch between privilege levels (it may be / was implemented with software-generated interrupts; int 80h
in older linux/BSD). User-space code asks kernel to do some work, for example read
from file or socket or write
to file. It also may ask kernel to run scheduler to switch to other task if there is any - with sched_yield syscall. If syscall code decides that request need some time and the task can't run further before the request is done (system call blocks - blocks current task from running; switches it state from TASK_RUNNING
的概念),它还会调用 OS 调度程序。
OS 调度程序可能会将当前任务保持为 运行ning(如果它处于 运行nable 状态)或者可能决定切换到其他任务(实际上是内核模式其他任务;还有 return-from-syscall 来恢复任务的 CS:IP+FLAGS) 并使用 switch_to
asm 宏来完成:http://lxr.free-electrons.com/source/arch/x86/include/asm/switch_to.h?v=4.6#L27:
33 /* \
34 * Context-switching clobbers all registers, so we clobber \
35 * them explicitly, via unused output variables. \
36 * (EAX and EBP is not listed because EBP is saved/restored \
37 * explicitly for wchan access and EAX is the return value of \
38 * __switch_to()) \
39 */ \
40 unsigned long ebx, ecx, edx, esi, edi; \
41 \
42 asm volatile("pushfl\n\t" /* save flags */ \
43 "pushl %%ebp\n\t" /* save EBP */ \
44 "movl %%esp,%[prev_sp]\n\t" /* save ESP */ \
45 "movl %[next_sp],%%esp\n\t" /* restore ESP */ \
46 "movl f,%[prev_ip]\n\t" /* save EIP */ \
47 "pushl %[next_ip]\n\t" /* restore EIP */ \
48 __switch_canary \
49 "jmp __switch_to\n" /* regparm call */ \
50 "1:\t" \
51 "popl %%ebp\n\t" /* restore EBP */ \
52 "popfl\n" /* restore flags */ \
53 \
如果只有一个非睡眠用户任务进入睡眠状态,则没有可见任务准备就绪 运行。但实际上有一个 pid 为 0 的不可见任务,有时称为 swapper 或 idle, which has lowest priority and always ready to run. It will run some special CPU instruction in loop to cool down the CPU - HLT;它还可以对事件/调用调度程序进行一些检查以查找 运行nable 任务。
一些奇怪的实时 OS(名称从 "V" 开始并以版本 5 结尾)没有 user-space/kernel-space 分离和隔离(所有代码 运行 都在环 0 中) 可以在没有系统调用或软件中断的情况下实现自愿上下文切换;但通常调用调度程序。
有用的链接:
- https://en.wikibooks.org/wiki/X86_Assembly/Advanced_Interrupts
- http://wiki.osdev.org/Interrupt
- http://wiki.osdev.org/Context_Switching
- http://www.informit.com/articles/article.aspx?p=364068 - How Multitasking Works at the Hardware Level, 2005, 非常好的书的章节 "Unabridged Pentium 4, The: IA32 Processor Genealogy".
- Task management on x86 提到 "Protected Mode Software Architecture" 书和 "Understanding the Linux Kernel, 3rd Ed.",Bovet,第 3 章过程,3.3.2。任务状态段。
- http://wiki.osdev.org/Task_State_Segment硬件任务切换
CPU can pause the execution of a certain program at any time and run another program. So how are the register values preserved during this step ? Are the registers pushed to stack or any other way ?
大多数处理器都定义了进程上下文块 (PCB) 结构,用于在暂停执行时保存进程寄存器。这样的处理器还有一个保存过程内容和加载过程上下文指令,可以一步保存或加载寄存器集。
寄存器没有压入堆栈。
进程切换类似于:
SAVEPROCESSCONTEXT address_of_running_process_pcb
LOADPROCESSCONTEXT address_of_new_process_pdb
在大多数系统中,中断由当时 运行 的任何进程提供服务,因此不会暂停进程。