(6.3)进程调度之Linux进程调度

1.Linux进程类型

  • 普通进程
    (1)采用动态优先级来调度
    (2)调度程序周期性地修改优先级(避免饥饿)
  • 实时进程
    (1)采用静态优先级来调度
    (2)由用户预先指定,以后不再改变

2.Linux进程的优先级

  • 静态优先级
    进程创建时指定或者由用户修改
  • 动态优先级
    在进程运行期间,可以按照调度策略改变;
    非实时进程采用动态优先级,由调度程序计算;
    只要进程占用CPU,优先级就随着时间流逝而不断减小;
    task_struct的counter表示动态优先级;

3.调度策略(结合task_struct结构)

  • task_struct->policy:指明进程调度策略
    在这里插入图片描述

  • 实时进程的调度策略
    (1)SCHED_FIFO(先进先出)
    当前实时进程一直占用CPU直到退出或者阻塞或者被抢占;
    阻塞后再就绪时,被添加到同优先级队列的末尾;
    (2)SCHED_RR(时间片轮转)
    与其它实时进程以Round-Robin方共同使用CPU;
    确保同优先级的多个进程能共享CPU;

  • 普通进程(非实时进程)的调度策略
    SCHED_OTHER(动态优先级)
    counter成员表示动态优先级

  • 调度策略的改变
    (1)系统调用sched_setscheduler()改变调度策略
    (2)实时进程的子孙进程也是实时进程

4.进程调度的依据:task_struct

  • policy
    进程的调度策略,用来区分实时进程和普通进程;
    SCHED_OTHER(0) || SCHED_FIFO(1) || SCHED_RR(2)

  • priority
    进程(实时和普通)的静态优先级

  • rt_priority
    实时进程特有的优先级:rt_priority+1000

  • counter
    进程能连续运行的时间

5.动态优先级与counter

  • counter值的含义
    (1)进程能连续运行的时间,单位是时钟滴答tick:
    时钟中断周期tick为10ms,若counter=60,则能连续运行600ms
    (2)较高优先级的进程一般counter较大
    (3)一般把counter看作动态优先级
  • counter的初值与priority有关
    (1)普通进程创建时,counter的初值为priority
  • counter的改变
    始终中断tick时,当前进程的counter减1,直到为0被阻塞
  • 子进程新建时的counter
    新建子进程counter从父进程时间片counter中继承一半,目的是:放置用户无限制地创建后代进程而长期占有CPU资源
p表示新建子进程
current表示当前进程
p->counter=(current->counter+1)>>1;
current->counter>>=1;

6.调度时机

  • 中断处理过程中直接调用scheedule( )
    这是内核被动调度,中断由内核完成,什么时候完成中断由内核决定;
    时钟中断、I/O中断/系统调用和异常;
    内核被动调度的情形;
  • 中断处理过程返回用户态时,直接调用schedule()
    必须根据need_resched标记;need_resched表示需要调度的意思
  • 内核线程可以直接调用schedule()进程进程切换
    内核主动调度的情形
  • 用户态进程只能通过陷入内核后,在中断处理过程中被动调度
    必须根据need_resched标记

6.进程切换

  • 概念:
    (1)进程挂起当前CPU上的进程并恢复之前挂起的某个进程
    (2)任务切换,上下文切换
  • 进程切换与中断上下文切换的差别
    中断前后在同一进程上下文中,只是用户态转向内核态执行
  • 进程上下文包含了进程执行需要的所有信息
    (1)用户地址空间:程序代码段,数据段,用户堆栈
    (2)控制信息:进程描述符,内核堆栈
    (3)硬件上下文(注意:中断也要保存上下文,只是保存的方法不同

7.进程调度和切换的流程

  • schedule()函数
    (1)选择新进程next=pick_next_task(rq,prev);//进程调度算法
    (2)调用宏context_switch(rq,prev,next)切换进程上下文
    prev表示旧进程,next表示需要切换的新进程(被调度的新进程)
    调用switch_to(prev, next)切换上下文;
  • 两个进程A,B切换的基本过程如下
    (1)正在运行的用户态进程A
    (2)发生中断(eg:时钟中断)
    保存current当前进程的cs:eip/esp/eflags到内核堆栈(即:将当前进程的上下文保存到内核堆栈里面
    从内核堆栈装入ISR中断服务例程的cs:eip(入口地址)和ss:esp(堆栈);
    (3)SAVE_ALL//保存现场,已进入内核中断处理过程
    (4)中断处理过程中或中断返回钱调用了schedule()(即:将进程B选择为新的进程
    其中的switch_to做了进程的上下文切换
    (5)运行用户态进程B(因为B曾经通过以上的步骤被切换出去过),此时进程B放到了CPU中
    (6)RESTORE_ALL//恢复现场
    (7)iret//中断返回pop cs:eip/esp/eflags
    (8)继续运行用户态进程A
发布了582 篇原创文章 · 获赞 143 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/u011436427/article/details/104529587