进程—进程调度(1)

本文详细介绍了Linux内核中的上下文切换过程,包括中断、硬件如何保存和恢复进程状态,以及在中断服务程序中调度程序如何进行上下文切换。讨论了Linux进程调度策略,包括实时进程和普通进程的区别,以及调度相关的系统调用。还涵盖了调度时机,如时间片用尽、进程状态变化等,并讲解了计算调度优先级的方法。
摘要由CSDN通过智能技术生成

进程—进程调度(1)


上下文切换

进程可以调度,但必须保证每个进程都可以顺序的执行,而一个进程执行所需的全部信息可由进程的PCB(task_struct)维护,所以在进程发生切换的时候可以将当前进程的运行状态信息(快照)保存到它的PCB中(这样就能在下一次调度程序选择到它时接着上一状态继续执行),将马上要执行的进程的运行状态信息(在PCB中)恢复,这样就可以合理的完成调度,这个过程就叫上下文切换。

中断

上下文切换是在内核中完成的,对用户透明,所以在上下文切换的时候必须先陷入内核(一般是通过时钟中断和系统调用)。上下文的切换需要硬件的支持。当前进程正在运行,当中断发生时,中断硬件将程序计数器、程序状态字、有时还有一个或多个寄存器(进程的运行状态信息)压入当前进程的内核堆栈中,PC随即跳转到中断服务程序入口(根据硬件向量法或软件查询法得到中断服务程序入口地址)去执行中断服务程序。注意,这些工作都是硬件完成的,在这些工作完成的同时完成了一次堆栈切换(从进程的用户堆栈切换到进程的内核堆栈,由中断硬件完成)。然后,PC的控制权转移到了软件(中断服务程序),一般地,中断服务程序有一个自己的中断堆栈(就像进程有自己的内核堆栈一样),为了不破坏内核堆栈,在执行中断服务程序的过程中又会发生一次堆栈切换(从内核堆栈切换到中断堆栈,这次的切换是软件完成的),进入到中断上下文之中,随后中断服务程序会调用处理特定中断请求的中断处理例程,让中断处理例程运行在中断上下文之中。中断处理例程完成中断处理之后返回到中断服务程序,返回的过程又会涉及到一次堆栈切换(由中断堆栈切换到内核堆栈,这次的切换也是软件完成的),最后,中断服务程序执行中断返回指令,由中断硬件将内核堆栈中的状态信息恢复到相应的寄存器中,在恢复寄存器的同时清空内核堆栈中保存的状态信息,系统从内核态返回到用户态,这里又发生了一次堆栈切换(从内核堆栈切换到用户堆栈,由中断硬件完成)。

发生上下文切换的中断过程

中断服务程序调用中断处理例程,中断处理例程在执行的过程中调用了调度程序进行调度,这时,调度程序会检查是否需要发生上下文切换(不是每次中断都会发生上下文的切换,例如某个时钟中断就可能不会导致上下文切换),发现需要发生上下文切换。接下来,要完成不同进程之间的内核栈的切换。为什么要切换内核栈呢?因为restore_all需要从内核栈中恢复中断现场,每个进程都有一个内核堆栈,它们互不相同,里面保存了各自的中断现场,所以要想恢复进程A的中断现场就得先切换到进程A的内核堆栈中去。

参考《Linux内核代码情景分析》第三章

ENTRY(ret_from_intr)
  ...
 jne ret_with_reschedule //需要返回用户态
  ...

ret_with_reschedule:
 cmpl $0,need_resched(%ebx)
 jne reschedule
 cmpl $0,sigpending(%ebx)
 jne signal_return  
restore_all:
  RESTORE_ALL

reschedule:
   call SYMBOL_NAME(schedule)//完成任务的调度以及内核堆栈的切换
   ...
   jmp ret_from_intr  




#define RESTORE_ALL \

popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
1: popl %ds; \
2: popl %es; \
addl $4,%esp; \
3: iret; \

/*
忽略信号处理等流程,可以看到,中断返回用户态的流程大概是
ret_from_intr-->[jne]ret_with_reschedule-->[jne]reschedule-->[call]schedule
-->[jmp]ret_from_intr-->[jne]ret_with_reschedule-->restore_all。
除了schedule是用call指令被调用之外,其他的流程都是通过跳转指令到达,所以不会返回。

在第一次执行ret_from_intr处的代码时,跳转条件满足,流程跳到ret_with_reschedule,一直
到[call]schedule,因为schedule是函数调用,所以会在内核堆栈中构造返回地址,调整%ebp,
%esp。这样,在schedule返回时,pc能跳到返回地址【call SYMBOL_NAME(schedule)的下一条指
令】,内核堆栈恢复到调用schedule之前的状态,流程回到call SYMBOL_NAME(schedule)的下一
条指令,跳来跳出,最后会跳到restore_all,恢复中断现场。
*/

内核堆栈的切换由内联汇编代码switch_to完成。

参考《深入理解Linux内核》中文第三版.第三章和第七章

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;

  /*
  内核进程的task_struct->mm为NULL, task_struct->active_mm指向之前进程的active_mm
  用户进程的task_struct->mm不为空,task_struct->active_mm指向自己的mm

  如果next是一个内核进程,那么他就是用prev的active_mm,不进行cr3的切换,使用prev进程
  的页目录。

  如果next是一个用户进程,那么他需要将cr3切换,使cr3寄存器指向自己的页目录
  */

  /*
  可以去include\asm-i386\mmu_context.h中查看enter_lazy_tlb函数和switch_mm函数
  */
 if (unlikely(!mm)) {    //next是一个内核进程
     next->active_mm = oldmm;
     atomic_inc(&oldmm->mm_count);
     enter_lazy_tlb(oldmm, next);
 } else  //next是一个用户进程
     switch_mm(oldmm, mm, next);
  /*
  static inline void switch_mm(struct mm_struct *prev,
                            struct mm_struct *next,
                            struct task_struct *tsk)
     ...
         //Re-load page tables 
         load_cr3(next->pgd); //切换cr3
     ...
 }   
   */

...

 /* Here we just switch the register state and the stack. */
 switch_to(prev, next, prev); //完成内核堆栈的切换

 return prev;
}

#define switch_to(prev,next,last) do {                   \

 unsigned long esi,edi;                      \
 asm volatile("pushfl\n\t"                   \
          "pushl %%ebp\n\t"                  \
          "movl %%esp,%0\n\t"    /* save ESP */      \
          "movl %5,%%esp\n\t"    /* restore ESP */   \
          "movl $1f,%1\n\t"     /* save EIP */      \
          "pushl %6\n\t"         /* restore EIP */   \
          "jmp __switch_to\n"                \
          "1:\t"                     \
          "popl %%ebp\n\t"          
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值