Linux内核设计与实现 第四章
1. 什么是调度
现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计算机上同时运行的各个任务(也就是进程)。
这个管理程序就是调度程序,功能:
- 决定哪些进程运行,哪些进程等待
- 决定每个进程运行多长时间
此外,为了获得更好的用户体验,运行中的进程还可以立即被其他更紧急的进程打断。
总之,调度是一个平衡的过程。一方面,它要保证各个运行的进程能够最大限度的使用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
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(这里的vruntime是进程虚拟运行的时间的总和。vruntime定义在:kernel/sched_fair.c 文件的 struct sched_entity 中),通过update_curr()函数更新进程的vruntime。
2)选择具有最小vruntime的进程投入运行。(注1)
3)进程运行完后,更新进程的vruntime,转入步骤2) (注2)
注1. 这里有点不好理解,根据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处理时间。
注2.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() |
暂时让出处理器 |
Linux内核设计与实现 第四章的更多相关文章
- linux及安全《Linux内核设计与实现》第一章——20135227黄晓妍
<linux内核设计与实现>第一章 第一章Linux内核简介: 1.3操作系统和内核简介 操作系统:系统包含了操作系统和所有运行在它之上的应用程序.操作系统是指整个在系统中负责完成最基本功 ...
- 《linux内核设计与实现》第一章
第一章Linux内核简介 一.unix 1.Unix的历史 Unix是现存操作系统中最强大和最优秀的系统. ——1969年由Ken Thompson和Dernis Ritchie的灵感点亮的产物. — ...
- 《深入理解Linux内核》阅读笔记 --- 第四章 中断和异常
1.中断的作用:中断信号提供了一种方式,使处理器转而去运行正常控制流之外的代码.当一个中断信号到达时,CPU必须停止它当前所做的事,并切换到一个新的活动.为了做到这一点,就要在内核态堆栈保存程序计数器 ...
- Linux内核设计与实现 第十七章
1. 设备类型 linux中主要由3种类型的设备,分别是: 设备类型 代表设备 特点 访问方式 块设备 硬盘,光盘 随机访问设备中的内容 一般都是把设备挂载为文件系统后再访问 字符设备 键盘,打印机 ...
- linux及安全《Linux内核设计与实现》第二章——20135227黄晓妍
第二章:从内核出发 2.1获取源代码 2.1.1使用git Git:内核开发者们用来管理Linux内核源代码的控制系统. 我们使用git来下载和管理Linux源代码. 2.1.2安装内核源代码(如果使 ...
- Linux内核设计与实现 第三章
1. 进程和线程 进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作都是由内核来实现的. Linux中的进程于Windows相比是很轻量级的,而且不严格区分进程和线程,线程不过是一种特殊的 ...
- Linux内核设计与实现 第五章
1. 什么是系统调用 系统调用就是用户程序和硬件设备之间的桥梁. 用户程序在需要的时候,通过系统调用来使用硬件设备. 系统调用的存在意义: 1)用户程序通过系统调用来使用硬件,而不用关心具体的硬件设备 ...
- 《linux内核设计与实现》第二章
第二章 从内核出发 一.获取内核源码 1.使用Git(linux创造的系统) 使用git来获取最新提交到linux版本树的一个副本: $ git clone git://git.kernel.org/ ...
- 《Linux内核分析》之第四章读书笔记
4.1多任务 多任务操作系统:同时并发地交互执行多个进程的操作系统 多任务操作系统会使多个进程处于堵塞或者睡眠状态.这些任务尽管位于内存,但是并不处于可运行状态.这些进程利用内核堵塞自己,直到某一事件 ...
随机推荐
- godaddy.com 都转到 www.dnspod.cn
我们在godaddy上注册了域名,但因为godaddy对域名解析的诸多限制和缓慢. 所以必需把域名服务器更改为dnspod上,然后再在dnspod上做A记录,CNAME等设置都很方便.
- January 15th, 2018 Week 03rd Monday
We got things to do. Places to go. People to see. Futures to make. 我们有很多事情要做,有很多地方要去,有很多人要见,有很多美好的未来 ...
- Python3编写网络爬虫04-爬取猫眼电影排行实例
利用requests库和正则表达式 抓取猫眼电影TOP100 (requests比urllib使用更方便,由于没有学习HTML系统解析库 选用re) 1.目标 抓取电影名称 时间 评分 图片等 url ...
- Djando 的 cmd命令
1.黑窗口命令 安装Django pip install django==1.11.16 -i https://pypi.doubanio.com/simple/ 创建项目 django-ad ...
- esp8266(0) AT指令
http://www.shaoguoji.cn/2017/01/15/ESP8266-usage/ 注意事项 使用WiFi模块的一些细节及注意事项 模块使用3.3V供电,一定注意电源的稳定,一些USB ...
- centos7搭建elasticsearch
Elasticsearch:负责日志检索和分析,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等 Logstash:对日志进行收集.过 ...
- Java:内省(Introspector)
内省(Introspector) 是Java 语言对 JavaBean 类属性.事件的一种缺省处理方法. JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且 ...
- Topographic ICA as a Model of Natural Image Statistics(作为自然图像统计模型的拓扑独立成分分析)
其实topographic independent component analysis 早在1999年由ICA的发明人等人就提出了,所以不算是个新技术,ICA是在1982年首先在一个神经生理学的背景 ...
- Vue-Vue文本渲染三种方法 {{}}、v-html、v-text
{{ }} 将元素当成纯文本输出 v-htmlv-html会将元素当成HTML标签解析后输出 v-textv-text会将元素当成纯文本输出 代码: <!DOCTYPE html> < ...
- angularjs自定义指令Directive
今天学习angularjs自定义指令Directive.Directive是一个非常棒的功能.可以实现我们自义的的功能方法. 下面的例子是演示用户在文本框输入的帐号是否为管理员的帐号"Adm ...