文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Linux进程切换

Linux进程切换

时间:2006-03-06  来源:ChinaE_OS

为了能保证不同的进程在CPU上运行,内核必须做到挂起正在CPU上运行的进程,唤醒其他进程,并且使其在CPU上正常运行。这个过程叫进程却换,或者上下文切换,任务切换。

1:硬件上下文

尽管每个进程都有自己的地址空间,但是所有的进程都共享CPU寄存器。所以当唤醒进程执行时,必须加载唤醒进程寄存器值到相应的寄存器。

硬件上下文是进程执行上下文的一个子集,Linux中一部分硬件上下文保存在进程描述符,一部分保存在内核栈。

进程切换非常频繁,所以一定要尽量减少保存和加载寄存器的时间。Linux2。6使用软件完成进程却换。进程切换只发生在内核态,进程切换的时候,所有用户态使用的寄存器内容被保存在内核栈,包括ss和esp,他们指明了用户态栈指针。

 

2:任务状态段(Task state segment(TSS))

80x86有一个特殊的段(任务状态段)保存硬件上下文,尽管Linux2。6不使用硬件上下文切换,但是必须为系统中不同的CPU建立TSS.,每个TSS都一个8位的TSS描述符,该描述符包括一个32位的Base域(指向TSS的开始地址)和一个20位的Limit域,S标示该TSS是否是系统段,0表示是系统段。

Linux创建的TSSD保存在全局描述符表(GDT),它的基地址保存在CPU的gdtr寄存器,tr寄存器保存着TSS的TSSD选择子。

Linux中用tss_struct描述TSS,代码如下:

struct tss_struct {

   unsigned short   back_link,__blh;

   unsigned long esp0;

   unsigned short   ss0,__ss0h;

   unsigned long esp1;

   unsigned short   ss1,__ss1h; /* ss1 is used to cache MSR_IA32_SYSENTER_CS */

   unsigned long esp2;

   unsigned short   ss2,__ss2h;

   unsigned long __cr3;

   unsigned long eip;

   unsigned long eflags;

   unsigned long eax,ecx,edx,ebx;

   unsigned long esp;

   unsigned long ebp;

   unsigned long esi;

   unsigned long edi;

   unsigned short   es, __esh;

   unsigned short   cs, __csh;

   unsigned short   ss, __ssh;

   unsigned short   ds, __dsh;

   unsigned short   fs, __fsh;

   unsigned short   gs, __gsh;

   unsigned short   ldt, __ldth;

   unsigned short   trace, io_bitmap_base;

   /*

    * The extra 1 is there because the CPU will access an

    * additional byte beyond the end of the IO permission

    * bitmap. The extra byte must be all 1 bits, and must

    * be within the limit.

    */

   unsigned long io_bitmap[IO_BITMAP_LONGS + 1];

   /*

    * Cache the current maximum and the last task that used the bitmap:

    */

   unsigned long io_bitmap_max;

   struct thread_struct *io_bitmap_owner;

   /*

    * pads the TSS to be cacheline-aligned (size is 0x100)

    */

   unsigned long __cacheline_filler[35];

   /*

    * .. and then another 0x100 bytes for emergency kernel stack

    */

   unsigned long stack[64];

} __attribute__((packed));

 

3: thread域

在进程切换的时候,Linux不能将硬件上下文保存在TSS,因为Linux为每个处理器设计一个TSS,而不是为每个进程设计一个TSS。

 

每个进程描述符包含一个thread域(thread_struct)。进程切换的时候,内核保存硬件上下文在thread域中。

struct thread_struct {

/* cached TLS descriptors. */

   struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];

   unsigned long esp0;

   unsigned long sysenter_cs;

   unsigned long eip;

   unsigned long esp;

   unsigned long fs;

   unsigned long gs;

/* Hardware debugging registers */

   unsigned long debugreg[8];  /* %%db0-7 debug registers */

/* fault info */

   unsigned long cr2, trap_no, error_code;

/* floating point info */

   union i387_union i387;

/* virtual 86 mode info */

   struct vm86_struct __user * vm86_info;

   unsigned long    screen_bitmap;

   unsigned long    v86flags, v86mask, saved_esp0;

   unsigned int     saved_fs, saved_gs;

/* IO permissions */

   unsigned long *io_bitmap_ptr;

/* max allowed port in the bitmap, in bytes: */

   unsigned long io_bitmap_max;

};

4:switch_to宏

该宏是与硬件相关的。宏中有三个参数:prev, next以及last,其中前两个是输入参数,后一个是输出参数。Prev指向将被替换进程的进程描述符,next指向将被执行的进程描述符。(1)介绍last的功能:

   不是所有的进程却换都是两个参数,有的进程切换涉及到三个参数,如:A进程被切换出去,而B进程被激活,prev指向A进程描述符,next指向B进程描述符。一旦A进程无效,A的控制流就停止了。然后,当内核又切换掉进程C(一般C和B进程不是同一个进程),激活进程A,prev指向进程C,next指向进程A。当A唤醒后,它找到它以前的内核栈,其prev指向A,next指向B。这时,执行在A进程一般的调度器失去了对C的参照,而事实上,该参照对进程的切换非常有用。

Last的用途:保存C进程描述符内容。

(2)       保存步骤:

Ø        在进程切换前,将prev指向的内容保存在eax寄存器,即:prev在A进程的内核栈上分配。

Ø        当进程切换后,当A唤醒后,将eax的内容写回到A进程的内存空间,A进程通过第三个参数last标示。因为进程切换过程中,CPU的寄存器是不变的,内存空间获取C进程的描述符。在schedule中,last标示A进程的prev变量,所以prev的地址被C描述符地址覆盖。

Switch_to的代码如下:

#define switch_to(prev,next,last) do {            \

   unsigned long esi,edi;              \

   asm volatile("pushfl\n\t"           \

           "pushl %%ebp\n\t"             \

           "movl %%esp,%0\n\t"  /* save ESP */      \

           "movl %5,%%esp\n\t"  /* restore ESP */   \

           "movl $1f,%1\n\t"    /* save EIP */      \

           "pushl %6\n\t"    /* restore EIP */   \

           "jmp __switch_to\n"           \

           "1:\t"               \

           "popl %%ebp\n\t"              \

           "popfl"              \

           :"=m" (prev->thread.esp),"=m" (prev->thread.eip), \

            "=a" (last),"=S" (esi),"=D" (edi)       \

           :"m" (next->thread.esp),"m" (next->thread.eip),  \

            "2" (prev), "d" (next));          \

} while (0)
相关阅读 更多 +
排行榜 更多 +
试着飞手游下载

试着飞手游下载

休闲益智 下载
血染小镇(功能菜单)中文版下载

血染小镇(功能菜单)中文版下载

飞行射击 下载
泰坦之旅高爆版下载

泰坦之旅高爆版下载

角色扮演 下载