linux 2.6源代码情景分析笔记之进程7

为了控制进程执行,内核必须有能力挂起正在cpu上运行的进程,并恢复以前挂起的某个进程的执行(进程切换、任务切换、上下文切换)。
每个进程可以拥有自己的地址空间,但所有进程必须共享cpu寄存器。在恢复一个进程的执行之前,内核必须确保每个寄存器装入了挂起进程时的数值。
进程恢复执行前必须装入寄存器的一组数据成为硬件上下文(hardware context)。硬件上下文是进程可执行上下文的一个子集,因为可执行上下文包含进程执行时所需要的所有信息。进程硬件上下文的一部分存放在TSS段,而剩余部分存放在内核态堆栈中。
进程切换只发生在内核态。在执行进程切换之前,用户态进程使用的所有寄存器内容都已保存在内核态堆栈上,这也包括ss和esp这对寄存器的内容(存储用户态堆栈指针的地址)。

80x86体系结构包含特殊段类型:任务段类型(task state segment,TSS)存放硬件上下文。linux并不使用硬件上下文切换,但是强制它为系统中每个不同的cpu创建一个TSS.
当80x86的一个cpu从用户态切换到内核态时,就从TSS中获取内核态堆栈的地址。
当用户态进程试图通过in或者out指令访问一个i/o端口时,cpu需要访问存放在TSS中的i/o许可权位图(permission bitmap)以检查该进程是否访问端口的权力。

当进程在用户态下执行in或者out指令时,控制单元执行下列操作:
1.它检查eflags寄存器中的2位iopl字段。如果该字段为3,控制单元就执行i/o指令。
2.访问tr寄存器以确定当前的tss和相应的i/o许可权位图。
3.检查i/o指令中指定的i/o端口在i/o许可权位图中对应的位。如果该位清零,这条i/o指令就执行,否则控制单元产生一个general protection异常。

下面的结构描述TSS格式。init_tss数组为系统上每个不同的cpu存放一个TSS.在每次进程切换时,内核都更新TSS的某些字段以便相应的cpu控制单元可以安全地检索到它所需要的信息。tSS反映了cpu上的当前进程的特权级,但不必为没有在运行的进程保留TSS.每个TSS有自己的8字节的任务状态段描述符(task state segment descriptor,TSSD).这个描述符包括指向TSS起始地址的32位base字段,20位limit字段。tssd的s标志位被清0,以表示相应的TSS是系统段的事实。
type字段置为11或者9以表示这个段实际上是一个TSS.在intel原始设计中,系统中的每个进程都应当指向自己的TSS;type字段的第二个有效位叫做busy位;如果进程正由cpu执行,则该位置1,否则置0.linux设计中,每个cpu只有一个TSS,因此busy位总是置为1.

linux创建的TSSD存放在全局描述符表(GDT)中,GDT的基地址存放在每个cpu的gdtr寄存器中。每个cpu的tr寄存器包含相应tss的tssd选择符,也包含了两个隐藏的非编程字段:tssd的base字段和limit字段。这样,处理器就能直接对tss寻址而不用从GDT中检索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));

进程每次切换时,被替换进程的硬件上下文必须保存在别处。不能如intel原始设计那样保存在TSS中,linux是为每个处理器而不是为了每个进程使用TSS.
每个进程描述符包含一个类型为thread_struct的thread字段,只要进程被切换出去,内核就把其硬件上下文保存在这个结构中。此结构包含的字段设计大部分cpu寄存器,但是不包括eax,ebx这些通用寄存器,它们的数值保留在内核堆栈中。

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;
};

#define INIT_THREAD  {                                                  /
        .vm86_info = NULL,                                              /
        .sysenter_cs = __KERNEL_CS,                                     /
        .io_bitmap_ptr = NULL,                                          /
}

/*
 * Note that the .io_bitmap member must be extra-big. This is 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.
 */
#define INIT_TSS  {                                                     /
        .esp0           = sizeof(init_stack) + (long)&init_stack,       /
        .ss0            = __KERNEL_DS,                                  /
        .ss1            = __KERNEL_CS,                                  /
        .ldt            = GDT_ENTRY_LDT,                                /
        .io_bitmap_base = INVALID_IO_BITMAP_OFFSET,                     /
        .io_bitmap      = { [ 0 ... IO_BITMAP_LONGS] = ~0 },            /
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值