> Linux教程 > Linux学习 >

进程切换小结

linux与任何分时系统一样,通过一个进程到另一个进程的切换,达到表面上多个进程同时执行的神奇效果。然而进程之间是如何切换的,并且怎样去决定切换进程。

相关知识:

进程链表
在多处理器中,每个CPU都有自己的对应的进程描述符双向链表。进程链表的头是指向init_task 描述符(进程0或者swapper进程)。linux将每个CPU下对应的可运行链表,按优先权划分为多个队列,提高调度程序的运行速度。系统也维护多个事件等待队列,等待队列中然而检索给定PID的进程描述符信息,顺序扫描进程描述符链表并检查给定PID相当低效,为了提高检索性能,利用hash散列表实现不同类型的PID.
linux中通过进程描述符来记录进程的详细信息(task_struct结构体),描述符中的相关字段和嵌套的数据结构信息比较复杂,不做解释(网络资源)。linux系统中进程的数量有上限。通过PID可以标识系统中每个执行上下文。


进程切换
为了控制进程的执行,内核必须有能力挂起正在CPU执行的进程,恢复以前挂起的进程执行。
所有进程共享CPU寄存器,进程恢复时必须装入寄存器的一组数据(硬件上下文TSS)。在进程切换时,首先要保存挂起进程的硬件上下文,同时要装载唤醒进程的硬件上下文。这块linux实现的是使用软件执行进程切换。这样可以通过move指令检查装入寄存器值的合法性。
进程描述符中的thread字段,包含了大部分的CPU寄存器,eax,ebx这些通用的寄存器保存在内核堆栈中。

进程切换执行的操作:

切换页全局目录安装一个新的地址空间。
切换内核堆栈和硬件上下文(包含新进程所需的所有信息,寄存器的值)
进程切换的第二步主要由switch_to宏完成。switch_to 宏需要三个参数(prev,next,last) ,采用内联汇编语言编码。进行相应寄存器的装载,然后调用__switch_to() C语言函数,此函数从寄存器获取参数(prev_p, next_p)
贴上源码:
struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
    struct thread_struct *prev = &prev_p->thread,
                 *next = &next_p->thread;
    int cpu = smp_processor_id();
    struct tss_struct *tss = &per_cpu(init_tss, cpu);

    /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */

    __unlazy_fpu(prev_p);

    /*
     * Reload esp0, LDT and the page table pointer:
     */
    load_esp0(tss, next);

    /*
     * Load the per-thread Thread-Local Storage descriptor.
     */
    load_TLS(next, cpu);

    /*
     * Save away %fs and %gs. No need to save %es and %ds, as
     * those are always kernel segments while inside the kernel.
     */
    asm volatile("movl %%fs,%0":"=m" (*(int *)&prev->fs));
    asm volatile("movl %%gs,%0":"=m" (*(int *)&prev->gs));

    /*
     * Restore %fs and %gs if needed.
     */
    if (unlikely(prev->fs | prev->gs | next->fs | next->gs)) {
        loadsegment(fs, next->fs);
        loadsegment(gs, next->gs);
    }

    /*
     * Now maybe reload the debug registers
     */
    if (unlikely(next->debugreg[7])) {
        loaddebug(next, 0);
        loaddebug(next, 1);
        loaddebug(next, 2);
        loaddebug(next, 3);
        /* no 4 and 5 */
        loaddebug(next, 6);
        loaddebug(next, 7);
    }

    if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr))
        handle_io_bitmap(next, tss);

    return prev_p;
}`




(责任编辑:IT)