进程切换小结
时间:2019-05-05 13:23 来源:linux.it.net.cn 作者:IT
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)
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) |