关键内容
调度算法
- O(n)
- O(1)
- CFS
- BFS
文件系统
- 日志文件系统的运行
互斥
- 临界区的三个原则
- 死锁的四个必要条件
- 管程的三种类型
7 进程管理与单处理器调度
7.1 进程管理
- 进程的抽象:独立的逻辑控制流和私有的地址空间
- 进程管理系统调用:fork exec exit waitpid getpid
- 僵尸进程、孤儿进程
- fork嵌套逻辑
7.2 单处理器调度
- 非抢占调度:必须等待当前进程主动放弃CPU;抢占调度相反
- 区分周转时间、就绪等待时间、响应时间
- FCFS:先来先服务
- SJF:短作业优先
- SRT:最短剩余时间,支持抢占
- HRRN:最高响应比优先,响应比为等待时间占总时间的比例
- RR:时间片轮转算法
- MQ:多级队列,按权限调度
- MLFQ:多级反馈队列,一个时间片内未完成的任务降低优先级
- FSS:公平共享调度
7.3 实时调度
- 实时体现在:任务有截止时间,例如周期性任务
- 静态优先级、动态优先级:同一任务的优先级是否会不同;一般可抢占
- RM:速率单调调度算法,周期越短优先级越高
- EDF:最早截止时间优先算法,执行离截止时间最近的任务
- LLF:最低松弛度优先,松弛度表示为截止时间-当前时间-还需运行时间
- 优先级反置现象:低优先级进程占据了高优先级进程需要的资源。解决:优先级继承、优先级天花板协议
8 多处理器调度
8.1 对称多处理与多核架构
- 超线程处理器:部分硬件共用,部分硬件可以并行
- 多核处理器、众核处理器
- 对称多处理器SMP、非一致内存访问系统NUMA
- 多处理器的缓存一致性问题
8.2 多处理器调度简述
-
SQMS:单队列多处理器调度(所有进程在一个队列中,不同核心选择队列不同位置的进程)
-
缓存亲和性:尽可能让进程保持在同一个CPU上运行
-
MQMS:多队列多处理器调度,每个CPU的调度队列独立
- 问题:2核3进程时,负载不均
- 解决:定期迁移进程,每个队列观测其他队列是否有更多进程,有则可以窃取,需要设置合理的窃取检查间隔
8.3 Linux O(1)调度
- O(n)调度算法
- 每个时间片开始时计算进程的动态优先级,按动态优先级执行
- 单队列,多个核竞争访问一个runqueue
- O(1)调度
- 每个CPU一个runqueue
- 140级全局优先级,实时进程优先级高于普通进程
- 维护140个优先级队列,和一个140长度的位图,通过位图最高1位查询来得知当前最高优先级的进程
- 优先级队列使用FIFO queue实现
- 每个优先级包括Active队列和Expired队列,进程执行完一个时间片重新计算自身优先级并进入Expired队列
- Active全空后与Expired交换
- 所有操作均为O(1)
8.4 Linux CFS 调度
- CFS:Completely Fair Scheduler
- 为每个进程维护vruntime,表示进程应该获得的CPU时间量,每次调度vruntime最小的进程
- 根据进程的nice值决定每次运行后vruntime的增长速度,nice越低,权重越高,vruntime增加越慢
- 类似stride策略
- 数据结构上采用红黑树记录vruntime
- 新进程vruntime设置为所在队列的最小值
- 休眠进程需要补偿vruntime
- 进程在多个CPU之间迁移时要对当前CPU的本地min_vruntime做适配
8.5 Linux/FreeBSD BFS 调度
- BFS:Brain F**k Scheduler
- 时间片轮转算法的变种
- 多处理机时使用单就绪队列,用双向链表实现
- 需要互斥访问
- 不需要负载均衡
- 每个进程有时间片大小和虚拟截止时间,虚拟截止时间表示在此时间到达前必须受到调度;每个时间片用完时重新计算虚拟截止时间
- EEVDF:最早可执行虚拟截止时间优先调度算法
9 文件系统
9.1 文件和文件系统
- 文件操作的基本单位是数据块
- 文件的访问模式:顺序访问、随机访问、索引访问(例:数据库)
- 文件描述符:文件描述符表的索引值
- 每个进程有一个文件描述符表,指向整个内核维护的一个打开文件表,打开文件表指向i-node表,i-node表指向具体文件
- 目录:特殊的文件,是文件名的线性列表,每个文件名包含一个指针
- 硬链接:直接指向相同inode;不能跨文件系统,不能链接到目录
- 软链接:新建文件,文件内部存储了链接的文件的名称;可以跨文件系统,可以链接到目录
- 文件系统需要先挂载(将根节点与
/
关联)才能访问
9.2 文件系统的设计和实现
- 虚拟文件系统VFS:文件系统的统一接口
- 文件系统的存储视图
- 文件卷控制块(超级块)
- 文件控制块(inode)
- 目录项
- 数据块
- 文件数据块分配:连续分配与链式分配
- 索引分配:包括一个索引块,索引块记录了后续属于此文件的块
- 用位图记录空闲块表
9.3 支持崩溃一致性的文件系统
-
文件系统中很多写操作不是原子的(例如:写位图、写inode、写数据块),容易引发崩溃不一致
-
fsck文件系统检查:
- 检查超级块
- 检查inode和位图是否匹配,不匹配以inode为准
- 检查inode,擦除损坏的inode
- 目录链接计数检查
- 重复指针检查
- 坏块检查
-
日志文件系统
- 日志写入:写入TxB(事务开始)、元数据、数据
- 日志提交:写入TxE
- 加检查点:将更新内容写入磁盘位置
- 通过日志超级块、周期清空日志来提高性能
- 元数据日志:日志只记录元数据,先写数据再写日志
- 三种模式:Journal Mode写全部日志,Ordered Mode只写元数据日志但保证先写数据后写日志,Writeback Mode只写元数据日志且不保证先写数据再写日志
10 进程间通信
10.1 进程间通信(IPC)概述
- 直接通信/间接通信:是否依赖内核中转
- IPC机制:
- 信号
- 管道
- 消息队列
- 套接字
- 文件
- 共享内存:唯一一种直接通信方法,其他均需要内核转达
- 缓冲区大小决定发送方是否需要等待接收方、需要等待多久
- 管道:有读写端的一定大小的字节队列,读端只读,写端只写,两端fd不同
- 命名管道:FIFO,可以通过mkfifo命令创建,命名管道可以支持任意两个进程间的通信,匿名管道只支持父子或兄弟进程(需要由某个进程显式创建)
- 消息队列:以消息的形式组织IPC信息,消息之间组成FIFO队列
- msgget msgsnd msgrcv msgctl
- msgget需要一个key,可以使用ftok生成
- 共享内存:将一个物理内存区域映射到多个进程的地址空间
- shmget shmat shmdt shmctl
- 信号
- 信号处理流程:先进入内核态,内核态将用户态返回地址指向预先定义的sig_handler函数,切入用户态执行,用户态执行完通过系统调用sigreturn陷入内核态,内核态再返回应用程序的正常上下文
11 线程与协程
11.1 线程
- 进程:资源分配的单位,线程:CPU调度的单位
- 线程的独有资源只有寄存器和栈
- 创建线程:pthread_create
- 等待线程:pthread_join,阻塞当前线程直到目标线程终止
- 线程的几种实现方式:
- 用户态管理、用户态运行
- 内核态管理、用户态运行
- 内核态管理、内核态运行(内核线程)
- 混合管理运行
- 用户态管理线程:使用用户函数而非系统调用管理线程,高效,TCB由用户管理
- 内核态管理的用户线程:TCB由内核管理,但运行的是用户代码
- 多线程程序使用fork可能出现问题
- 内核线程:处理内核问题,例如进行缓存写回、虚存交换、文件系统写日志
- LWP:轻量级线程或混合线程,用内核线程和用户级线程一一对应
11.2 协程
- 协程:子例程的推广,子例程只允许一个入口点、返回一次,协程可以多个入口点、在中途挂起和恢复(核心在于主动让出控制流)
- 无栈协程也可以看成一种异步函数
- 按控制传递机制分类:对称协程/非对称协程
- 无栈协程:可以挂起/恢复的函数;有栈协程:用户态管理并运行的线程
- First-Class协程:协程可以作为变量,可以用于数据结构、传参等;Second-Class协程无法作为实际对象
- 切换需求
- 进程:页表、堆、栈、寄存器
- 线程:栈、寄存器
- 协程:寄存器
12 同步互斥
12.1 概述
-
临界区:
- 进入去:检查能否进入临界区的代码
- 临界区:访问临界资源,需要互斥执行
- 退出区:清除正在进入临界区的标志
- 剩余区
-
临界区三原则:空闲则入,忙则等待,有限等待,(可选)让权等待
-
实现互斥的方法之一:禁用硬件中断
-
Peterson算法:孔融让梨
-
Dekkers算法
-
n线程:
-
更高级的原语:
- 锁
- 原子测试置位指令TaS:测试一个值是否是1,无论是否都置1,返回原始值,可用来请求锁(是1就忙等,是0就置1)
- 原子交换指令CaS:判断值是否是old,如果是则改为new返回true,否则什么也不做,返回false
12.2 信号量
- P:请求,减少;V:释放,增加
- 两种使用模式
- 临界区:先P再V
- 条件同步:我P你V
- 生产者-消费者问题:设置3个信号量,一个用于临界区限制,一个用于计数当前满槽数,一个用于计数当前空槽数
12.3 管程和条件变量
-
管程:一种用于多线程互斥访问共享资源的程序结构,使用面向对象方法,任意时刻最多一个线程执行管程代码
-
条件变量:没有实际变量语义,但有阻塞/唤醒功能,允许线程在条件变量处被阻塞、等待条件变量唤醒
-
管程各原语的语义:
-
Hoare/Hansen/MESA管程
- Hoare:消费者进入,消费者入等待队列,生产者进入,生产者生产,生产者通知消费者后进入紧急队列,消费者消费,消费者离开,生产者离开
- Mesa:消费者进入,消费者入等待队列,生产者进入,生产者生产,生产者通知消费者可用,生产者离开,消费者离开队列重新进入管程,消费者消费(允许竞争)
- Hausen:消费者进入,消费者等待,生产者进入,生产者生产,生产者离开,生产者通知,消费者消费,消费者离开
-
生产者-消费者问题的Java条件变量实现分析
1 | class BoundedBuffer { |
这里的consume
和produce
函数均为管程,二者同时只有一个可以执行,这是为了防止二者同时进入,依赖各自的条件变量导致死锁
12.4 同步互斥实例问题
-
哲学家就餐
- 解决方案:AND型信号量集
-
读者—写者问题:读优先/写优先?
12.5 死锁
-
资源:可重用资源、可消耗资源
-
资源分配图
- 顶点:进程和资源
- 边:进程指向资源为请求,资源指向进程为分配
-
形成死锁的必要条件:互斥,持有并等待,非抢占,循环等待
-
死锁预防:破坏死锁四个条件之一
-
死锁避免
-
需要先验信息:进程声明所需资源的最大数目
-
系统安全状态:存在安全执行序列,沿此序列,每个进程释放资源后,下一个进程一定可以满足需求
-
不安全状态不一定会出现死锁,但安全状态一定无死锁
-
银行家算法
-
-
死锁检测:同银行家算法
-
死锁恢复
- 中止一些进程直到死锁消除
- 资源抢占