天天看点

进程切换进程切换

为了控制进程的执行,内核必须有能力挂起在cpu上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换,任务切换或上下文切换。

尽管每个进程可以拥有属于自己的地址空间,但所有进程必须共享cpu寄存器。因此,在恢复一个进程执行前,内核必须确保每个寄存器装入了挂起进程时的值。

进程恢复执行前必须装入寄存器的一组称为硬件上下文(hardware context)。硬件上下文是进程可执行上下文的一个子集,因为可执行上下文包含进程执行时需要的所有信息。在linux中,进程硬件上下文的一部分放在tss段,而剩余的部分存放在内核态信息堆栈中。

进程切换只发生在内核态。在执行进程切换之前,用户态进程使用的所有寄存器内容都已保存在内核态堆栈上。

80x86体系结构包括了一个特殊的段类型,叫任务状态段(task state segment,tss)来存放硬件上下文。尽管linux并不使用硬件上下文切换,但是强制它为系统中每个不同的cpu创建一个tss。主要有两个理由:

1、当80x86的一个cpu从用户态切换到内核态时,它就从tss中获取内核态堆栈的地址。

2、当用户态进程试图通过in或者out指令访问一个i/o端口是,cpu需要访问存放在tss中的i/o许可权位图(permission bitmap)以检查进程是否有访问端口的权利。

thread字段

在每次进程切换时,被替换进程的硬件上下文必须保存在别处。不能像intel原始设计那样把它保存在tss中,因为linux为每个处理器而不是为每个进程使用tss。

因此,每个进程描述符包含一个类型为thread_struct的thread字段,只要进程切换出去,内核就把其他硬件上下文保存在这个结构中。

进程切换可能只发生在精心定义的点:schedule()函数。

从本质上说,每个进程切换由两步组成:

1、切换页全局目录以安装一个新的地址空间;

2、切换内核态堆栈和硬件上下文,因为硬件上下文提供了内核执行新进程所需要的所有信息,包含cpu寄存器。

ia-32架构处理器在程序应用中提供16个基本程序指令寄存器,可以被分为以下几组:

? general-purpose registers. these eight registers are available for storing operands and pointers.

  ? eax — accumulator for operands and results data

  ? ebx — pointer to data in the ds segment

  ? ecx — counter for string and loop operations

  ? edx — i/o pointer

  ? esi — pointer to data in the segment pointed to by the ds register; source pointer for string operations

  ? edi — pointer to data (or destination) in the segment pointed to by the es register; destination pointer for

          string operations

  ? esp — stack pointer (in the ss segment)

  ? ebp — pointer to data on the stack (in the ss segment)

? segment registers. these registers hold up to six segment selectors.

? eflags (program status and control) register. the eflags register report on the status of the program

          being executed and allows limited (application-program level) control of the processor.

? eip (instruction pointer) register. the eip register contains a 32-bit pointer to the next instruction to be

       executed.

linux-2.6.11.1/include/asm-i386/system.h

pushf/pushfd push eflags onto stack

popf/popfd pop eflags from stack

at&t汇编中,命令中可以指定操作范围,如pushb是将一个byte压栈,而pushw就是将一个word压栈,同样pushl就是压栈long(也就是双字)

这个程序做了以下工作:

1、在eax和edx寄存器中分别保存prev和next的值

2、把eflags和esp寄存器的内容保存在prev内核栈中。

3、把esp的内容保存到prev->thread.esp中以使该字段指向prev内核栈的栈顶

4、把next->thread.esp装入esp。此时内核开始在next的内核栈上操作,因此这条指令实际上完成了从prev到next的切换。由于进程描述符的地址和    内核栈的地址紧挨着,所以改变内核栈意味着改变当前进程。

5、把标记为1的地址存入prev->thread.eip。当被替换进程重新恢复执行时,进程执行被标记为1的那条指令。

6、宏把next->thread.eip的值压入next的内核栈。

7、跳到__switch_to()函数

8、这里被替换进程再次获得cpu;它执行一些指令恢复eflags和ebp寄存器的内容指令

9、拷贝eax寄存器的内容到第三个参数last标识的内存区域

eax寄存器指向刚被替换的进程的描述符

继续阅读