Linux内核设计与实现 四、 进程调度

作用:在可运行态进程之间分配有限的处理器时间资源的内核子系统。

4.1 多任务

多任务操作系统

概念:
 同时并发地交互执行多个进程的操作系统

分类:
●抢占式多任务:
 所有Unix的变体和许多现代操作系统一样,Linux提供了抢占式的多任务模式
抢占:由调度程序来决定什么时候停止一个进程的运行,以便其他进程能够得到执行计划,这个强制的挂起动作叫作抢占。
 进程在被抢占之前能够运行的时间是预先设置好的,这个事件叫做时间片。(Linux未采用)

●非抢占式多任务:
 非抢占式多任务模式下,除非进程自己主动停止运行,否则它会一直执行。
让步:进程主动挂起自己的操作称为让步。一个决不做出让步的悬挂进程就能使系统崩溃。

4.2 Linux的进程调度

第一版到2.4版本:原始设计

相当简陋,难以胜任

2.5版本-2.6.23:O(1)调度程序

对于大服务器的工作负载很理想,但是在很多交互程序要运行的桌面系统则表现不佳,因为其缺少交互进程。

2.6.23开始:完全公平调度算法(CFS)

4.3 策略

策略决定调度程序何时让什么进程运行

I/O消耗型和处理器消耗型进程

I/O消耗型进程通常运行一小会,然后阻塞在等待I/O请求,多数GUI都属于I/O密集型。

Linux为了保证交互式应用和桌面系统的性能,更倾向于优先调度I/O消耗型进程。

进程优先级

两种不同的优先级范围:
●nice值:
 默认为0,越大的nice值意味着更低的优先级

●实时优先级:
 越高的实时优先级数值意味着进程优先级越高。

时间片

概念:时间片是一个数值,表明进程在被抢占前所能持续运行的时间。

长短:过长会导致系统对交互响应迟钝,过短会增大进程切换带来的处理器耗时

作用:多数操作系统中,是否要将一个进程立刻投入运行(抢占当前进程),是完全由进程优先级和是否有时间片决定的。

调度策略

理想的调度器:
 假如文本编辑器和视频编码器进程同时运行,有两个目标:
1.文本编辑程序获得更多的处理器时间,因为它属于交互式应用,在需要时应该确保它得到处理器时间。
2.文本编辑器能在被唤醒时抢占视频解码程序

4.4 Linux调度算法

CFS

概念
 CFS完全摒弃时间片而是分配给进程一个处理器使用比重。通过这种方式,CFS确保了进程调度中能有恒定的公平性。

做法
 CFS的做法是允许每个进程运行一段时间、循环轮转、选择运行最少的进程作为下一个运行进程,而不采用时间片的做法。
最小粒度
 当可运行任务数量趋于无限时,各自获得的处理器使用比趋于0,会造成不可接受的切换消耗。CFS为此引入了每个进程获得的时间片底线,默认为1ms。

完全公平?
 CFS不是完美的公平,只是近乎完美的多任务。但是确实在多进程环境下降低了调度延迟带来的不公平性。

4.5 Linux调度的实现

四个组成部分:
1.时间记账
2.进程选择
3.调度器入口
4.睡眠和唤醒

时间记账

调度器实体结构
 名为se的成员变量,嵌入在进程描述符struct task_struct内。进程描述符见第三章

虚拟运行时间
 vruntime变量存放进程的虚拟运行时间(加权后的)。CFS使用vruntime变量来记录一个程序到底运行了多长时间以及它还应该再运行多久。

进程选择

进程选择
 CFS会挑一个具有最小vruntime的进程来运行,这是CFS调度算法的核心。
 CFS使用红黑树来组织可运行进程队列。

●挑选下一个任务:利用红黑树迅速找到vruntime值最小的进程
●向树中加入进程:在进程变为可运行状态(被唤醒)或者是通过fork()调用第一次创建进程,CFS将进程加入rbtree。
●从树中删除进程:删除动作发生在进程堵塞(不可运行态)或终止时(结束运行)

调度器入口

 进程调度的主要入口点是函数schedule(),schedule()通常要和一个具体的调度类相关联。

 该函数中唯一重要的事情是调用pick_next_task()。pick_next_task()会以优先级为序,从高到低依次检查每一个调度类,并从最高优先级的调度类中选择最高优先级的进程。

睡眠和唤醒

休眠
 进程把自己标记成休眠状态,从可执行红黑树中移出,放入等待队列。

休眠的两种状态
●TASK_INTERRUPTIBLE:接收到信号会被提前唤醒并响应该信号
●TASK_UNINTERRUPTIBLE:忽略信号

等待队列
 休眠通过等待队列进行处理。等待队列是由等待某些事件发生的进程组成的简单链表。

过程:代码见书4.5.4
进程把自己放入等待队列中并设置成不可执行状态。当与等待队列相关的事件发生后,队列上的进程会被唤醒。还要注意避免产生竞争条件。

唤醒:
函数wake_up()唤醒指定的等待队列上的所有进程
虚假唤醒
 一种可能性是条件变量的等待被信号中断
 另一种可能性是唤醒后被另一个进程抢先了。
在这里插入图片描述

4.6 抢占和上下文切换

上下文切换
 从一个可执行进程切换到另一个可执行进程。

 有两项基本工作:
 ●调用switch_mm(),该函数负责把虚拟内存从上一个进程映射切换到新进程中
 ●调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。包括保存、恢复栈信息和寄存器信息等。

内核调用schedule()
原因:不能只靠用户程序代码显示调用schedule(),可能会永远执行下去。

标志:need_resched表明是否需要重新执行一次调度。通过检查该标志来判断是否调用schedule()

用户抢占

发生情况
●从系统调用返回用户空间时
●从中断处理程序返回用户空间时

如何抢占
返回的时候查看need_resched标志,来判断是否调用schedule()

内核抢占

Linux的特点
 支持内核抢占,其他大部分Unix变体和大部分操作系统不支持内核抢占,内核代码可以一直执行到完成。

调度的安全性
 只要重新调度是安全的,内核就可以在任何时间抢占正在执行的任务。
 只要没有持有锁,内核就可以进行抢占。

发生情况
●中断处理程序正在执行,且返回内核空间之前。
●内核代码再一次具有可抢占性的时候
●内核中的任务显式调用schedule()
●内核中的任务阻塞

4.7 实时调度策略

两种实时调度策略

静态优先级
 两种实时算法都是静态优先级,不实时计算动态优先级。保证给定优先级别的实时进程总能抢占优先级比它低的进程。

两种策略具体见书
●SCHED_FIFO:简单、先入先出,不基于时间片,可以一直执行下去,只有更高优先级的任务才能抢占
●SCHED_RR:带有时间片的SCHED_FIFO

软实时工作方式
 内核调度进程,尽力使进程在它的限定时间来前运行,但内核不保证总能满足这些进程的要求。但是Linux的实时调度算法的性能还是不错的

4.8 与调度相关的系统调用

与调度策略和优先级相关

与处理器绑定有关

放弃处理器时间

作用:
 sched_yield()系统调用,让进程显式地将处理器时间让给其他进程。

原理:
 将进程从活动队列中移到过期队列中,确保在一段时间内它都不会再执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值