1、上下文
一般来说,CPU在任何时刻都处于以下三种情况之一:
(1)运行于用户空间,执行用户进程;
(2)运行于内核空间,处于进程上下文;
(3)运行于内核空间,处于中断上下文。
应用程序通过系统调用陷入内核,此时处于进程上下文。现代几乎所有的CPU体系结构都支持中断。当外部设备产生中断,向CPU发送一个异步信号,CPU调用相应的中断处理程序来处理该中断,此时CPU处于中断上下文。
在进程上下文中,可以通过current关联相应的任务。进程以进程上下文的形式运行在内核空间,可以发生睡眠,所以在进程上下文中,可以使作信号量(semaphore)。实际上,内核经常在进程上下文中使用信号量来完成任务之间的同步,当然也可以使用锁。
中断上下文不属于任何进程,它与current没有任何关系(尽管此时current指向被中断的进程)。 由于没有进程背景,在中断上下文中不能发生睡眠,否则又如何对它进行调度。所以在中断上下文中只能使用锁进行同步,正是因为这个原因,中断上下文也叫做原子上下文(atomic context) (关于同步以后再详细讨论)。在中断处理程序中,通常会禁止同一中断,甚至会禁止整个本地中断,所以中断处理程序应该尽可能迅速,所以又把中断处理分成上部和下部(关于中断以后再详细讨论)。
2、上下文切换
上下文切换,也就是从一个可执行进程切换到另一个可执行进程。上下文切换由函数context_switch()函数完成,该函数位于kernel/sched.c中,它由进程调度函数schedule()调用。
2.2、用户抢占
当内核即将返回用户空间时,内核会检查need_resched是否设置,如果设置,则调用schedule(),此时,发生用户抢占。一般来说,用户抢占发生几下情况:
(1)从系统调用返回用户空间;
一般来说,CPU在任何时刻都处于以下三种情况之一:
(1)运行于用户空间,执行用户进程;
(2)运行于内核空间,处于进程上下文;
(3)运行于内核空间,处于中断上下文。
应用程序通过系统调用陷入内核,此时处于进程上下文。现代几乎所有的CPU体系结构都支持中断。当外部设备产生中断,向CPU发送一个异步信号,CPU调用相应的中断处理程序来处理该中断,此时CPU处于中断上下文。
在进程上下文中,可以通过current关联相应的任务。进程以进程上下文的形式运行在内核空间,可以发生睡眠,所以在进程上下文中,可以使作信号量(semaphore)。实际上,内核经常在进程上下文中使用信号量来完成任务之间的同步,当然也可以使用锁。
中断上下文不属于任何进程,它与current没有任何关系(尽管此时current指向被中断的进程)。 由于没有进程背景,在中断上下文中不能发生睡眠,否则又如何对它进行调度。所以在中断上下文中只能使用锁进行同步,正是因为这个原因,中断上下文也叫做原子上下文(atomic context) (关于同步以后再详细讨论)。在中断处理程序中,通常会禁止同一中断,甚至会禁止整个本地中断,所以中断处理程序应该尽可能迅速,所以又把中断处理分成上部和下部(关于中断以后再详细讨论)。
2、上下文切换
上下文切换,也就是从一个可执行进程切换到另一个可执行进程。上下文切换由函数context_switch()函数完成,该函数位于kernel/sched.c中,它由进程调度函数schedule()调用。
static
inline
task_t * context_switch(runqueue_t * rq, task_t * prev, task_t * next)
{
struct mm_struct * mm = next -> mm;
struct mm_struct * oldmm = prev -> active_mm;
if (unlikely( ! mm)) {
next -> active_mm = oldmm;
atomic_inc( & oldmm -> mm_count);
enter_lazy_tlb(oldmm, next);
} else
switch_mm(oldmm, mm, next);
if (unlikely( ! prev -> mm)) {
prev -> active_mm = NULL;
WARN_ON(rq -> prev_mm);
rq -> prev_mm = oldmm;
}
/* Here we just switch the register state and the stack. */
switch_to(prev, next, prev);
return prev;
}
其中,switch_mm()将虚拟内存映射到新的进程;switch_to完成最终的进程切换,它保存原进程的所有寄存器信息,恢复新进程的所有寄存器信息,并执行新的进程。无论何时,内核想要进行任务切换,都通过调用schedule()完成任务切换。
task_t * context_switch(runqueue_t * rq, task_t * prev, task_t * next)
{
struct mm_struct * mm = next -> mm;
struct mm_struct * oldmm = prev -> active_mm;
if (unlikely( ! mm)) {
next -> active_mm = oldmm;
atomic_inc( & oldmm -> mm_count);
enter_lazy_tlb(oldmm, next);
} else
switch_mm(oldmm, mm, next);
if (unlikely( ! prev -> mm)) {
prev -> active_mm = NULL;
WARN_ON(rq -> prev_mm);
rq -> prev_mm = oldmm;
}
/* Here we just switch the register state and the stack. */
switch_to(prev, next, prev);
return prev;
}
2.2、用户抢占
当内核即将返回用户空间时,内核会检查need_resched是否设置,如果设置,则调用schedule(),此时,发生用户抢占。一般来说,用户抢占发生几下情况:
(1)从系统调用返回用户空间;