内核资料收集
内核能挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为称为进程切换,任务切换或上下文切换。
1. 硬件上下文
每个进程拥有属于自己的地址空间,所有进程共享CPU寄存器。恢复一个进程的执行之前,内核必须确保每个寄存器装入
了挂起进程时的值。
进程恢复执行前,必须装入寄存器的一组数据称为硬件上下文。硬件上下文是进程可执行上下文的一个子集。80x86体系
结构的linux中,进程硬件上下文的一部分存放在TSS段,剩余部分存放在内核态堆栈中。
用prev变量表示切换出的进程的描述符,next表示切换进的进程的描述符。则进程切换就可以定义成这样的行为:保存prev
硬件上下文,用next硬件上下文代替prev。因为进程切换经常发生,因此减少保存和装入硬件上下文所花费的时间非常重要。
早期linux版本利用80x86体系结构所提供的硬件支持,通过far jmp指令跳到next进程TSS描述符的选择符来执行进程切换。
执行这条指令时,CPU通过自动保存原来的硬件上下文,装入新的硬件上下文来执行硬件上下文切换。
基于以下两点原因,linux2.6使用软件执行进程切换:
a. 通过mov指令逐步执行切换,可以较好控制所装入数据的合法性。尤其是可以检查ds和es段寄存器的值成为可能,这
些值可能被恶意伪造。用单独的far jmp指令时,不能进行此检查。
b. 两种方法所需时间大致相同。当前的切换代码还可改进,却不能对硬件上下文切换进程优化。
进程切换只发生在内核态。在执行进程切换前,用户态进程使用的所有寄存器内容都已保存在内核态堆栈上,包括ss和esp
等寄存器的内容。
2. 任务状态段(TSS)
80x86体系结构包括了一个特殊的段类型,叫任务状态段(Task State Segment, TSS)来存放硬件上下文。尽管Linux并不使用
硬件上下文切换,但强制它为系统中每个不同的CPU创建一个TSS。这样做的主要理由:
a. 当80x86的CPU从用户态切换到内核态时,它从TSS中获取内核态堆栈地址。
b. 用户态进程通过in/out指令访问一个i/o端口时,CPU需要访问存放在TSS中的i/o许可权位图,以检查该进程是否有访问
端口的权限。
tss_struct结构描述TSS的格式。init_tss为每个不同的CPU存放一个TSS。每次进程切换时,内核都更新TSS的某些字段以便相
应的CPU控制单元可以安全的检索到它需要的信息。因此,TSS反映了CPU上的当前进程的特权级,但不必为没在运行的进程保
留TSS。
在intel的原始设计中,系统的每个进程都应该有自己的TSS。linux设计中,每个CPU只有一个TSS
......
3. thread字段
进程切换时,被替换的进程的硬件上下文必须保存在别处。不能像intel原始设计的那样保存在TSS段,因为Linux为每个CPU而不
是进程使用TSS。因此每个进程描述符包含一个类型为thread_struct的thread字段。
进程切换出去,内核就将硬件上下文保存在thread结构字段。
4. 执行进程切换
进程切换可能只发生在精心定义的点:schedule()函数。
本质上,进程切换由两步组成:
a. 切换页全局目录,以安装一个新的地址空间
b. 切换内核态堆栈和硬件上下文,因为硬件上下文提供了内核执行新进程所需要的所有信息,包含CPU信息。
4.1. switch_to宏
进程切换时,内核态堆栈和硬件上下文的切换由switch_to宏执行。
原型:switch_to(prev, next, last)
三个参数中,prev/next分别表示,被替换进程和新进程描述符地址在内存中的位置。last是输出参数,记录上一个进程的
描述符地址。至于为什么要用三个参数,需要仔细消化。内核代码中用法如下:
switch_to(prev, next, prev)
4.2. 保存和加载FPU/MMX/XMM寄存器
......
阅读(590) | 评论(0) | 转发(0) |