linux----进程切换

xiaoxiao2021-02-28  96

进程切换 一、进程调度 1、调度时机 1)自愿的调度随时可以进行 ##内核空间:一个进程可以通过schedule()启动一次调度/在调用该函数之前,将本进程的状态设置为TASK_INTERRUPIBLE 或 TASK_UNINTERRUPTIBLE,暂时放弃运行进入睡眠;为自愿的暂时放弃运行加上时间限制的系统调用:schedule_timeout() ##用户空间:通过系统调用 pause();为自愿的暂时放弃运行加上时间限制的系统调用:nanosleep() 两者的区别:自愿放弃运行在用户空间可见,在内核空间不可见 2)非自愿:这种调度发生在进程从系统空间返回到用户空间的前夕 二、进程切换 1、硬件支持:Intel在i386系统结构中增设任务状态段TSS,保存要切换的进程大的所有信息,计划为每一个进程准备一个TSS,将它存放在TR寄存器中;但是Linux内核并没有这么做,每一个CPU拥有一个TSS,一经装入就不再变了,原因在于:改变TSS中的SS0和ESP0所花的开销比通过装入TR以更换一个TSS要小得多。因为不是为每个进程分配TSS,所以配替换的进程的硬件上下文是保存在进程描述符中的thread_struct类型的字段。 TSS段的结构在Processor.h (include\asm-i386) 中,如下定义: 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)); 在文件Processor.h (include\asm-i386) 中给出了TSS定义 #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 },        \ } 2、执行进程切换 进程切换由schedule()函数执行的,主要分为两步 1) 切换页全局目录(Page Global Directory)来加载一个新的地址空间,实际上就是加载新进程的cr3寄存器值。 2) 切换内核堆栈和硬件上下文,这些包含了内核执行一个新进程的所有信息,包含了CPU寄存器。 3、linux进程的切换过程 最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程 - 正在运行的用户态进程X - 发生中断——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack). - SAVE_ALL //保存现场 - 中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换 - 标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行) - restore_all //恢复现场 - iret - pop cs:eip/ss:esp/eflags from kernel stack - 继续运行用户态进程Y 4、源码分析 在schedule()函数中,真正担当起进程切换的函数是context_switch(),在该函数中,它使用了一个很重要的宏函数switch_to()来实现进程切换,原来进程切换说到底关键在switch_to()上,看看swicth_to()的真面目: #define switch_to(prev,next,last) do {                    \     unsigned long esi,edi;                        \     /**      * 在真正执行汇编代码前,已经将prev存入eax,next存入edx中了。      */    /**      * 保存eflags和ebp到内核栈中。必须保存是因为编译器认为在switch_to结束前,      *  它们的值应当保持不变。       */     asm volatile("pushfl\n\t"                    \              "pushl %
转载请注明原文地址: https://www.6miu.com/read-72548.html

最新回复(0)