Linux内核学习笔记(六)进程调度

综述

内核调度子系统负责进程调度,调度程序决定让哪个进程运行、什么时候运行、运行多久。调度程序的目标有两个:一个是最大化系统资源的利用率,一个是减少和用户的交互延迟,让用户觉得多个进程在同时运行。这两个目标是冲突的,需要做trade-off。

多任务操作系统

多任务操作系统指的是指可以多个进程同时交错执行的操作系统。在单处理器机器上,用户会产生有多个进程同时在不同的处理器上在同时执行的错觉。多任务系统可以分成两种:协作式(cooperative)和抢占式(preemptive)多任务系统。包括Linux、Windows在内的大部分操作系统都属于抢占式多任务系统。

抢占式多任务操作系统中,调度程序决定一个进程什么时候停止执行。调度程序终止一个进程的执行(并不是这个进程自愿的)并让另一个进程执行的行为被称为抢占,进程在被抢占之前可以运行的时间一般由调度程序预先确定,被称为时间片(timeslice)。时间片一般是动态计算的,Linux没有直接使用时间片,而是使用了处理器比例(processor proportion)来规定进程运行时间。

协作式多任务操作系统中,调度程序无法决定进程可以运行多久,一个进程除非自己主动地暂停并放弃CPU,否则可以一直执行下去,独占CPU,不会出现抢占的情况。进程通常会主动暂停执行,否则系统可能就会宕机了。

I/O-Bound进程和Processor-Bound进程

进程可以分为I/O-Bound和Processor-Bound,前者代表进程花费大量时间提交和等待I/O请求,这种类型的进程通常执行一段很短的时间,然后阻塞等待新的I/O,大部分GUI应用属于I/O bound,虽然GUI应用很少读写磁盘,但是他们花费大量时间等待用户通过键盘和鼠标进行交互。Processor-Bound进程是花费大量时间执行代码的进程,通常只有被抢占的时候才会停止执行,因为他们不会因为I/O请求被阻塞。对于Processor-Bound进程,调度策略倾向于让进程以较少的运行次数执行较长的时间,即每次运行时间较长。

这两种类别并不是互斥的,进程可以同时具有这两类行为,X Windows Server既是I/O-Bound也是Processor-Bound。文字处理软件大部分时间在等待用户输入(I/O-Bound),但是某些时刻会进行拼写检查或者宏计算等(Processor-Bound)。

调度程序必须尝试同时满足两个互相冲突的目标:快速响应(低延时)和最大化系统利用率(高吞吐量)。为了选择“最有价值的”的进程执行并同时照顾其他低优先级的进程,调度器通常需要采用非常复杂的调度策略。Unix系统的调度策略通常优先考虑低延时目标,Linux为了确保优秀的交互响应和桌面系统性能也优先考虑低延时目标,但是Linux采用的调度策略并不会忽视Processor-Bound进程(稍后会说)。

进程优先级

基于优先级的调度算法是一种典型的调度算法,根据进程的价值和需求给进程一个合适的优先级,通常(Linux中不是这样做的),优先级高的进程先于优先级低的进程运行,优先级相同的进程使用循环(round-roubin)方式运行。此外,优先级高的进程还会获得更长的时间片。

Linux内核中进程可以分为互斥的两种:普通进程(normal process) 和 实时进程(read-time process)。后者的优先级永远高于前者,两者分别使用独立的优先级范围,只有当没有实时进程需要运行时,普通进程才能够获得调度。前者使用nice值,是一个-20到19之间的数值,nice值越大,优先级越低(对其他进程来说你很nice),nice值越小优先级越大(这个进程对其他进程来说不nice)。不同的Unix系统对nice值的处理是不一样的,Mac OS X中使用nice值确定时间片的绝对长度,Linux中,nice值控制时间片的分配比例(timeslice proportion),nice值越小,获得的比例越大(这里的比例可以理解度时间片的相对长度)。Linux中使用ps -el命令可以查看进程的nice值(NI列),值为“-”的进程是read-time process,没有nice值。

read-time proc

目录 1 进程的组织 5 1.1 进程相关数据结构 5 1.1.1 进程的基本信息 6 1.1.2 进程状态 10 1.1.3 TASK_RUNNING状态的进程链表 11 1.1.4 进程间关系 12 1.2 Linux的线程——轻量级进程 15 1.3 进程的创建——do_fork()函数详解 19 1.4 执行进程间切换 33 1.4.1 进程切换之前的工作 33 1.4.2 进程切换实务 —— switch_to宏 37 1.4.3 __switch_to函数 39 1.5 fork与vfock系统调用的区别 42 1.6 内核线程 46 1.7 挂起状态进程的组织 49 1.7.1 等待队列头 49 1.7.2 等待队列的操作 50 1.7.3 进程资源限制 55 1.8 系统调用execve() 56 1.8.1 拷贝用户态参数 57 1.8.2 重要的数据结构 61 1.8.3 search_binary_handler函数 66 1.8.4 目标文件的装载和投入运行 69 1.8.5 库函数 92 2 中断控制 94 2.1 中断的分类 94 2.2 中断的硬件环境 95 2.2.1 外部中断请求IRQ 95 2.2.2 中断描述符表 96 2.2.3 中断和异常的硬件处理 97 2.3 中断描述符表 99 2.3.1 中断门、陷阱门及系统门 99 2.3.2 IDT的初步初始化 100 2.4 异常处理 101 2.5 中断处理 106 2.5.1 中断向量 107 2.5.2 IRQ数据结构 108 2.5.3 do_IRQ()函数 113 2.5.4 中断服务例程 115 2.5.5 IRQ线的动态分配 116 2.6 下半部分 117 2.6.1 软中断 118 2.6.2 tasklet 121 2.6.3 工作队列 122 2.7定时器中断 124 2.7.1 时钟与定时器 124 2.7.2 定时器中断相关的数据结构 127 2.7.3 定时器中断的上半部分 129 3 进程调度 138 3.1 进程调度的概念 138 3.2 进程调度的数据结构和优先级 141 3.2.1 进程的优先级 141 3.2.2 数据结构 145 3.3 调度程序所使用的函数 151 3.3.1 scheduler_tick函数 151 3.3.2 try_to_wake_up函数 156 3.3.3 recalc_task_prio函数 160 3.4 schedule()函数 163 3.4.1 直接调用 163 3.4.2 延迟调用 164 3.4.3 进程切换之前所做的工作 168 3.4.4 完成进程切换时所执行的操作 171 3.4.5 进程切换后所执行的操作 173 3.5 多处理器运行队列的平衡 175 3.5.1 调度域 176 3.5.2 rebalance_tick()函数 178 3.5.3 load_balance()函数 180 3.5.4 move_tasks()函数 183 3.6 进程退出 187 3.6.1 进程终止 187 3.6.2 进程删除 189 4 进程的并发性体现 191 4.1 内核抢占 193 4.1.1 内核抢占概念 193 4.1.2 同步技术总揽 196 4.2 每CPU变量 197 4.3 原子操作 199 4.4 优化屏障和内存壁垒 203 4.4.1 优化屏障 204 4.4.2 内存壁垒 204 4.5 自旋锁 206 4.6 读写自旋锁 211 4.6.1 为读获取和释放一个锁 213 4.6.2 为写获取或释放一个锁 214 4.7 顺序锁 215 4.8 RCU机制 217 4.9 信号量 219 4.9.1 获取和释放信号量 221 4.9.2 读/写信号量 224 4.9.3 补充信号量 225 4.10 禁止本地中断 226 4.10.1 禁止本地中断 227 4.10.2 禁止下半部(可延迟函数) 229 4.11 一些避免竞争条件的实例 231 4.11.1 引用计数器 231 4.11.2 大内核锁 231 4.11.3 内存描述符读/写信号量 232 4.11.4 slab高速缓存链表的信号量 233 4.11.5 索引节点的信号量 233 4.12 内核同步与互斥的总结 233
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值