热门关键字:  ubuntu  分区  Fedora  linux系统进程  函数

当前位置 :| 主页>Linux教程>内核研究>

Linux进程:Linux切换机制主流程

来源: 作者: 时间:2008-08-07 Tag: 点击:

由于在内核中所有的内核段寄存器均为统一的,因此这里无需保存ES,CS,SS,DS,FS,GS;
CR3(Linux中没有使用LDT)已经在前面的switch_mm处理了;
由于Linux没使用TSS-previous task link field,其切换完全采用软件处理切换,故这里无需考虑TSS-previous task link field;
指令EIP会保存在task.thread.eip中,ESP会保存在task.thread.esp中,EBP,ESI,EDI会用显示指令入栈保存;
对于Linux而言,其仅仅使用到了CPU的4级机制中的0和3两级,使用方法如下:
当进程正运行在用户空间时,如果此时来了个中断,CPU将会执行:从TR->GDTR[i ]->TSS中取出当前的SS0:ESP0,从IDTR->IDT[i ]中取出执行代码CS:IP,将当前所有寄存器压到SS0:ESP0堆栈中,包括进程的SS3:ESP3,随后从CS:IP处开始执行代码。当中断代码执行完毕后,内核将会从进程堆栈中,将SS3:ESP3、CS:IP弹出,从而回到用户空间重新开始执行,此时并不需要CPU主动来切换级别了;从这里可知,CPU是需要TSS中的SS0和ESP0来进行高->低级别切换的,因此进程在切换时,必须要将自己的SS0和ESP0保存到TR-> GDTR[i ]->TSS的SS0和ESP0字段中去,其实,在Linux中,对于同一个CPU,所有的进程都使用一个TSS,只是在进程切换时,被切换到的进程将会把自己的ESP0保存到TSS.ESP0中去(在函数__switch_to中),那为什么不把自己的SS0也保存到TSS.SS0中呢,这是因为所有进程的SS0都是统一的,为内核的SS,而内核在初始化的时候,已经将该TSS.SS0设置为自己的SS,因此无需继续设置SS0;
至于EFLAGS为什么没有保存,这点在2.6中已经纠正,即执行了pushf和popf;
ECX为什么没有保存,则涉及到了如下的理由:i386 ABI / function calling sequence
Allregisters on the Intel386 are global and thus visible to both a callingand a called function. Registers %ebp, %ebx, %edi,%esi, and %esp'belong' to the calling function. In other words, a called functionmust preserve these registers' values for its caller. Remainingregisters 'belong' to the called function. If a calling function wantsto preserve such a register value across a function call, it must savethe value in its local stack frame.Some registers have assigned rolesin the standard calling sequence:
%esp: The stack pointerholds the limit of the current stack frame, which is the address of thestack’s bottom-most, valid word. At all times, the stack pointer shouldpoint to a word-aligned area.
......
%ecx and%edx:Scratch registers have no specified role in the standard callingsequence. Functions do not have to preserve their values for the caller.
......

Linux进程管理和X86进程管理的结合

从上面描述中,我们知道X86要求每个进程都必须有自己的TSS,在每次进程切换的时候,通过对应的TR[i ]+GDTRbase找到该进程的TSS,然后保存前一个任务的所有寄存器,同时将找到的TSS中所有的寄存器值恢复到系统对应的寄存器中,从而实现进程切换。
由于Linux实现进程切换的时候,并不采用该机制,但是却避不过X86这个机制,因此Linux内核设置了init_tss全局变量,在start_kernel-> trap_init->cpu_init初始化时,设置了对应的GDT[i ]指向该init_tss以及TR.index=i;此后,在Linux系统运行过程中,就再也不会改变TR以及该GDT[i ],对应X86来讲,就好像永远运行着1个进程;Linux使用自身的切换机制来实现了进程的切换,这些在上面的文章中已经说明,这里不在多说。
另一重要问题Linux必须面对:当从高级别切换到低级别时,会引起CPU运行级别的变化,例如从级别3到级别0,此时CPU需要获取0级别的SS0以及 ESP0(例如前面描述的当进程正运行在用户空间时来了个中断范例)来恢复SS:ESP,其设计方法就是从当前的TSS->SS0:ESP0中获取。为了适应CPU的这种设计,Linux内核在每次switch_to切换进程时,都将被切换来的进程的ESP0保存到init_tss.esp0中;另外由于Linux内核的SS0始终为KERNEL_SS保持不变,故无需每次切换都将其保存到init_tss.ss0中,只需要在Linux内核初始化时将init_tss.ss0设置为KERNEL_SS就可以了;



相关文章:
精通initramfs构建step by step
Linux利用kexec迅速切换内核
进程上下文VS中断上下文
内核通知链 学习笔记
linux spi子系统驱动分析
menuconfig 配置选项
《Linux操作系统内核实习》之练习一
udev详解
什么叫微内核,宏内核?
Linux 信号signal处理机制
开发简单的 Linux2.6 内核模块
删除内核的perl脚本
Linux2.6内核usb gadget驱动移植
GCC hacks in the Linux kernel
iomem
kernel学习的想法
让自己的驱动支持udev
linux内核编译步骤
内核的等待队列
Linux内核wait_queue深入分析
升级和删除内核
SD卡驱动分析2
Linux Kernel VDSO本地权限提升漏洞
内核中的TCP的追踪分析-15-TCP(IPV4)的客户端与
linux 2.6内核可加载模块的编译
内核模块HelloWorld
在环回接口上发送一个数据报
ARP初始化
1分钟编译FreeBSD内核
linux设备模型之uart驱动架构分析