主要内容:

  • 什么是调度
  • 调度实现原理
  • Linux上调度实现的方法
  • 调度相关的系统调用

1. 什么是调度

现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计算机上同时运行的各个任务(也就是进程)。

这个管理程序就是调度程序,它的功能说起来很简单:

  1. 决定哪些进程运行,哪些进程等待
  2. 决定每个进程运行多长时间

此外,为了获得更好的用户体验,运行中的进程还可以立即被其他更紧急的进程打断。

总之,调度是一个平衡的过程。一方面,它要保证各个运行的进程能够最大限度的使用CPU(即尽量少的切换进程,进程切换过多,CPU的时间会浪费在切换上);另一方面,保证各个进程能公平的使用CPU(即防止一个进程长时间独占CPU的情况)。

2. 调度实现原理

前面说过,调度功能就是决定哪个进程运行以及进程运行多长时间。

决定哪个进程运行以及运行多长时间都和进程的优先级有关。为了确定一个进程到底能持续运行多长时间,调度中还引入了时间片的概念。

2.1 关于进程的优先级

进程的优先级有2种度量方法,一种是nice值,一种是实时优先级。

nice值的范围是-20~+19,值越大优先级越低,也就是说nice值为-20的进程优先级最大。

实时优先级的范围是0~99,与nice值的定义相反,实时优先级是值越大优先级越高。

实时进程都是一些对响应时间要求比较高的进程,因此系统中有实时优先级高的进程处于运行队列的话,它们会抢占一般的进程的运行时间。

进程的2种优先级会让人不好理解,到底哪个优先级更优先?一个进程同时有2种优先级怎么办?

其实linux的内核早就有了解决办法。

对于第一个问题,到底哪个优先级更优先?

答案是实时优先级高于nice值,在内核中,实时优先级的范围是 0~MAX_RT_PRIO-1 MAX_RT_PRIO的定义参见 include/linux/sched.h

1611 #define MAX_USER_RT_PRIO        100
1612 #define MAX_RT_PRIO             MAX_USER_RT_PRIO

nice值在内核中的范围是 MAX_RT_PRIO~MAX_RT_PRIO+40 即 MAX_RT_PRIO~MAX_PRIO

1614 #define MAX_PRIO                (MAX_RT_PRIO + 40)

第二个问题,一个进程同时有2种优先级怎么办?

答案很简单,就是一个进程不可能有2个优先级。一个进程有了实时优先级就没有Nice值,有了Nice值就没有实时优先级。

我们可以通过以下命令查看进程的实时优先级和Nice值:(其中RTPRIO是实时优先级,NI是Nice值)

$ ps -eo state,uid,pid,ppid,rtprio,ni,time,comm
S   UID   PID  PPID RTPRIO  NI     TIME COMMAND
S     0     1     0      -   0 00:00:00 systemd
S     0     2     0      -   0 00:00:00 kthreadd
S     0     3     2      -   0 00:00:00 ksoftirqd/0
S     0     6     2     99   - 00:00:00 migration/0
S     0     7     2     99   - 00:00:00 watchdog/0
S     0     8     2     99   - 00:00:00 migration/1
S     0    10     2      -   0 00:00:00 ksoftirqd/1
S     0    12     2     99   - 00:00:00 watchdog/1
S     0    13     2     99   - 00:00:00 migration/2
S     0    15     2      -   0 00:00:00 ksoftirqd/2
S     0    16     2     99   - 00:00:00 watchdog/2
S     0    17     2     99   - 00:00:00 migration/3
S     0    19     2      -   0 00:00:00 ksoftirqd/3
S     0    20     2     99   - 00:00:00 watchdog/3
S     0    21     2      - -20 00:00:00 cpuset
S     0    22     2      - -20 00:00:00 khelper

2.2 关于时间片

有了优先级,可以决定谁先运行了。但是对于调度程序来说,并不是运行一次就结束了,还必须知道间隔多久进行下次调度。

于是就有了时间片的概念。时间片是一个数值,表示一个进程被抢占前能持续运行的时间。

也可以认为是进程在下次调度发生前运行的时间(除非进程主动放弃CPU,或者有实时进程来抢占CPU)。

时间片的大小设置并不简单,设大了,系统响应变慢(调度周期长);设小了,进程频繁切换带来的处理器消耗。默认的时间片一般是10ms

2.3 调度实现原理(基于优先级和时间片)

下面举个直观的例子来说明:

假设系统中只有3个进程ProcessA(NI=+10),ProcessB(NI=0),ProcessC(NI=-10),NI表示进程的nice值,时间片=10ms

1) 调度前,把进程优先级按一定的权重映射成时间片(这里假设优先级高一级相当于多5msCPU时间)。

假设ProcessA分配了一个时间片10ms,那么ProcessB的优先级比ProcessA高10(nice值越小优先级越高),ProcessB应该分配10*5+10=60ms,以此类推,ProcessC分配20*5+10=110ms

2) 开始调度时,优先调度分配CPU时间多的进程。由于ProcessA(10ms),ProcessB(60ms),ProcessC(110ms)。显然先调度ProcessC

3) 10ms(一个时间片)后,再次调度时,ProcessA(10ms),ProcessB(60ms),ProcessC(100ms)。ProcessC刚运行了10ms,所以变成100ms。此时仍然先调度ProcessC

4) 再调度4次后(4个时间片),ProcessA(10ms),ProcessB(60ms),ProcessC(60ms)。此时ProcessB和ProcessC的CPU时间一样,这时得看ProcessB和ProcessC谁在CPU运行队列的前面,假设ProcessB在前面,则调度ProcessB

5) 10ms(一个时间片)后,ProcessA(10ms),ProcessB(50ms),ProcessC(60ms)。再次调度ProcessC

6) ProcessB和ProcessC交替运行,直至ProcessA(10ms),ProcessB(10ms),ProcessC(10ms)。

这时得看ProcessA,ProcessB,ProcessC谁在CPU运行队列的前面就先调度谁。这里假设调度ProcessA

7) 10ms(一个时间片)后,ProcessA(时间片用完后退出),ProcessB(10ms),ProcessC(10ms)。

8) 再过2个时间片,ProcessB和ProcessC也运行完退出。

这个例子很简单,主要是为了说明调度的原理,实际的调度算法虽然不会这么简单,但是基本的实现原理也是类似的:

1)确定每个进程能占用多少CPU时间(这里确定CPU时间的算法有很多,根据不同的需求会不一样)

2)占用CPU时间多的先运行

3)运行完后,扣除运行进程的CPU时间,再回到 1)

3. Linux上调度实现的方法

Linux上的调度算法是不断发展的,在2.6.23内核以后,采用了“完全公平调度算法”,简称CFS。

CFS算法在分配每个进程的CPU时间时,不是分配给它们一个绝对的CPU时间,而是根据进程的优先级分配给它们一个占用CPU时间的百分比。

比如ProcessA(NI=1),ProcessB(NI=3),ProcessC(NI=6),在CFS算法中,分别占用CPU的百分比为:ProcessA(10%),ProcessB(30%),ProcessC(60%)

因为总共是100%,ProcessB的优先级是ProcessA的3倍,ProcessC的优先级是ProcessA的6倍。

Linux上的CFS算法主要有以下步骤:(还是以ProcessA(10%),ProcessB(30%),ProcessC(60%)为例)

1)计算每个进程的vruntime(注1),通过update_curr()函数更新进程的vruntime。

2)选择具有最小vruntime的进程投入运行。(注2)

3)进程运行完后,更新进程的vruntime,转入步骤2) (注3)

注1. 这里的vruntime是进程虚拟运行的时间的总和。vruntime定义在:kernel/sched_fair.c 文件的 struct sched_entity 中。

注2. 这里有点不好理解,根据vruntime来选择要运行的进程,似乎和每个进程所占的CPU时间百分比没有关系了。

1)比如先运行ProcessC,(vr是vruntime的缩写),则10ms后:ProcessA(vr=0),ProcessB(vr=0),ProcessC(vr=10)

2)那么下次调度只能运行ProcessA或者ProcessB。(因为会选择具有最小vruntime的进程)

长时间来看的话,ProcessA、ProcessB、ProcessC是公平的交替运行的,和优先级没有关系。

而实际上vruntime并不是实际的运行时间,它是实际运行时间进行加权运算后的结果。

比如上面3个进程中ProcessA(10%)只分配了CPU总的处理时间的10%,那么ProcessA运行10ms的话,它的vruntime会增加100ms。

以此类推,ProcessB运行10ms的话,它的vruntime会增加(100/3)ms,ProcessC运行10ms的话,它的vruntime会增加(100/6)ms。

实际的运行时,由于ProcessC的vruntime增加的最慢,所以它会获得最多的CPU处理时间。

上面的加权算法是我自己为了理解方便简化的,Linux对vruntime的加权方法还得去看源码^-^

注3.Linux为了能快速的找到具有最小vruntime,将所有的进程的存储在一个红黑树中。这样树的最左边的叶子节点就是具有最小vruntime的进程,新的进程加入或有旧的进程退出时都会更新这棵树。

其实Linux上的调度器是以模块方式提供的,每个调度器有不同的优先级,所以可以同时存在多种调度算法。

每个进程可以选择自己的调度器,Linux调度时,首先按调度器的优先级选择一个调度器,再选择这个调度器下的进程。

4. 调度相关的系统调用

调度相关的系统调用主要有2类:

1) 与调度策略和进程优先级相关 (就是上面的提到的各种参数,优先级,时间片等等) - 下表中的前8个

2) 与处理器相关 - 下表中的最后3个

系统调用

描述

nice()

设置进程的nice值

sched_setscheduler()

设置进程的调度策略,即设置进程采取何种调度算法

sched_getscheduler()

获取进程的调度算法

sched_setparam()

设置进程的实时优先级

sched_getparam()

获取进程的实时优先级

sched_get_priority_max()

获取实时优先级的最大值,由于用户权限的问题,非root用户并不能设置实时优先级为99

sched_get_priority_min()

获取实时优先级的最小值,理由与上面类似

sched_rr_get_interval()

获取进程的时间片

sched_setaffinity()

设置进程的处理亲和力,其实就是保存在task_struct中的cpu_allowed这个掩码标志。该掩码的每一位对应一个系统中可用的处理器,默认所有位都被设置,即该进程可以再系统中所有处理器上执行。

用户可以通过此函数设置不同的掩码,使得进程只能在系统中某一个或某几个处理器上运行。

sched_getaffinity()

获取进程的处理亲和力

sched_yield()

暂时让出处理器

转载于:https://www.cnblogs.com/Marineking/archive/2013/04/25/3043052.html

《Linux内核设计与实现》读书笔记(四)- 进程的调度相关推荐

  1. linux内核设计与实现看不懂,Linux内核设计与实现读书笔记

    Unix强大的根本原因: Unix简洁, 提供几百个系统调用, 设计目的明确 Unix中 所有东西都被当做文件对待 Unix内核和相关系统工具是用C语言开发的, 移植能力强大 Unix进程创建迅速, ...

  2. Linux内核设计与实现 读书笔记

    第二章 Linux内核 1 内核开发特点 1)内核编译时不能访问C库: 2)浮点数很难使用: 3)内核只有一个定长堆栈: 4)注意同步和并发. 第三章 进程管理 1 current宏:查找当前运行进程 ...

  3. 进程调度(一)--linux内核设计与实现读书笔记

    进程的调度程序是保证进程能有效工作的一个内核子系统.调度程序负责决定将哪个进程投入运行,何时运行以及运行多少时间.简单的来说,调度程序就是在给一堆就绪的进程分配处理器的时间,调度程序是多任务操作系统的 ...

  4. Linux内核设计与实现读书笔记

    1.进程管理 内核通过task_struct管理进程. 2.进程调度 1.可执行队列runqueue. 2.用户抢占.从中断返回或者系统调用返回的时候发生. 3.系统调用 1.系统调用参数放在寄存器里 ...

  5. linux+模块与设备关系,linux内核设计与实现读书笔记——设备和模块

    一.设备类型 1.块设备 blkdev:以块为单位寻址,支持重定位(数据随机访问),通过块设备节点来访问. 2.字符设备cdev:不可寻址,提供数据流访问,通过字符设备节点访问. 3.网络设备:对网络 ...

  6. Linux内核设计与实现学习笔记目录

    **注:**这是别人的笔记,我只是把目录抄过来 <Linux内核设计与实现学习笔记> 1.<Linux内核设计与实现>读书笔记(一)-内核简介 2.<Linux内核设计与 ...

  7. 初探内核之《Linux内核设计与实现》笔记上

    内核简介  本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核   原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单. 2. 高 ...

  8. linux内核双向循环队列,读书笔记之linux内核设计与实现(2)进程调度

    调度程序是内核的组成部分,它负责选择下一个要运行的进程.进程调度程序可看作在可运行态进程之间分配有限的处理器时间资源的内核子系统. 多任务操作系统就是能够同时并发的交互执行多个进程的操作系统.多任务系 ...

  9. 《Windows核心编程》读书笔记四 进程

    第四章 进程 本章内容 4.1 编写第一个Windows应用程序 4.2 CreateProcess函数 4.3 终止进程 4.4 子进程 4.5 管理员以标准用户权限运行时 进程定义为一个正在运行的 ...

  10. Linux内核设计与实现 总结笔记(第五章)系统调用

    系统调用 内核提供了用户进程和内核交互的接口,使得应用程序可以受限制的访问硬件设备. 提供这些接口主要是为了保证系统稳定可靠,避免应用程序恣意妄行. 一.内核通信 系统调用在用户空间进程和硬件设备之间 ...

最新文章

  1. 第十九课.Pytorch-geometric扩展
  2. EF Core Model更新迁移
  3. NET许可证及License
  4. 两雄争霸:解读MSU2019年度视频编码大赛
  5. pycharm中无法import已经安装的ros中的库
  6. codeforces1438 E.Yurii Can Do Everything
  7. 知方可补不足~sqlserver中使用ROW_NUMBER进行的快速分页
  8. IMail邮件服务-1
  9. 2020计算机原理组成1254,1254计算机科学与技术专业计算机组成原理A科目2020年09月国家开 放大学(中央广播电视大学)考试试题及答案.pdf...
  10. 实例:从一个文件中读取销售记录,并合并生成销售报告,实现销售册数,总销售额,平均售价...
  11. Linux从某个字符串处换行,Windows和Linux中任意字符串替换成换行符的问题
  12. 老程序员为什么从不使用 Java 自带的序列化?
  13. 【1002】Eclipse安装(编程软件)、Eclipse创建第一Java项目、eclipse安装包
  14. 从刷卡到二维码再到刷脸,无现金方式如何掀起支付革命?丨Xtecher 视角
  15. 电感电容之开关电源的原理
  16. java timer缺陷
  17. open3d使用知识拾遗
  18. 35、矩阵(稀疏矩阵)的压缩存储(一)
  19. Windows下实现快速访问GitHub
  20. KPI, Metrics, KPI的区别

热门文章

  1. Blender 2.8 【修改器】快捷键备忘录 学习笔记
  2. Linux操作系统———李纳斯
  3. 如何修改本地网站的域名
  4. java调用ip138实现ip地址查询
  5. 虚拟机操作工具--Xmanager5 下载/安装/使用
  6. HTML编程(网页的基本标签)
  7. 读书百客:《临江仙引·画舸》赏析
  8. Redis:只刷面试题,怎可能进大厂,多理解原理(RDB 持久化、AOF持久化)
  9. 老年程序员的出路何在
  10. mysql左连接查询分页_mysql多表关联查询分页如何优化?-问答-阿里云开发者社区-阿里云...