目录
操作系统之进程与线程(一)
进程
进程是什么
进程就是一个正在执行程序的实例 进程是独立的实体 有自己的程序计数器 和状态 它存放着程序正文 数据 以及其他资源的i地址空间 同时还有一个执行线程 该线程有PC 寄存器 堆栈等
进程的创建和终止
创建
- 系统初始化
- 执行了正在运行的进程所调用
- 用户请求创建一个新进程
- 一个批处理作业的初始化
新进程由老进程执行系统调用 创建
- unix中创建进程的唯一个方式是fork函数 但分两步 fork 然后再 execve 为了fork后进行文件描述符的修改 子进程的地址空间是父进程的副本
- windows 则含多个 createProcess同时做两件事 子进程地址空间和父进程不同
终止
- 正常退出
- 出错退出
- 严重错误
- kill
进程的层次结构
- unix: 进程只有一个父进程 隶属于一个进程(递归也是)的包括该进程一起组成一个进程组 发送一个信号给进程组 组内成员可以捕获 忽略 或被信号杀死
OS启动时 由init特殊进程启动 读入终端 为终端依次创建进程
windows: 进程创建时 父进程得到一个句柄 拥有句柄就可以控制子进程
进程的状态
- 运行态
- 就绪态
- 阻塞态
3种状态有4种转换关系 其中只有运行态和就绪态之间能够相互转换 其他的都是单向转换
进程的实现
OS内部维护一个进程表 每一个进程占一个表项(也叫进程控制块PCB) 表项中包含了 程序计数器 堆栈指针 内存分配状况 锁打开文件的状态 账号 和 调度信息
中断发生后OS的底层的工作步骤
- 硬件压入堆栈PC
- 硬件从中断向量装入新的PC
- 汇编保存寄存器值
- 汇编语言设置新的堆栈
- C中断服务例程启动
- 调度决定下一个进程
- C返回到汇编
- 汇编开始执行新线程
多道程序设计模型
CPU利用率 = 1 - p^n
- p: 等待I/O操作时间和停留于内存的时间比
- n: 内存中有n个进程
进程间的通信IPC
3个主要问题
- 一个进程如何把信息传递给另一个 shell 中一个进程的输出传给第二个进程
- 确保两个或更多的进程在关键活动中不会出现交叉
- 正确的顺序 A必须在B之前打印
竞争条件
多个进程读取公用存储区(共享数据) 最后结果取决于进程运行的精确时序
Murphy法则: 任何可能出错的地儿都会出错
临界区
对共享内存进行访问的程序片段 就叫临界区域
- 任何两个进程不能同时在临界区
- 不应对CPU的速度和数量进行任何假设
- 临界区外运行的进程不得阻塞其他进程
- 不得使进程无限期等待进入临界区
忙等待的互斥
- 屏蔽中断
进程进入临界区后立即屏蔽所有中断 包括时钟中断
但若屏蔽不打开 则很有影响 对于用户进程 屏蔽并不是一个合适的机制
- 锁变量
0 可进入 1 不可进入
但当一个进程发现为0 到准备设置为1的时候 进行了时钟中断 被切换了线程 仍会造成这样的问题
- 严格轮转法
用于忙等待的锁是自旋锁
- TSL指令
测试并加锁 将内存字读到寄存器RX 然后在该内存地址上存一个非零值 读写不可分割 指令结束前其他处理器无法访问内存字 执行TSL的CPU锁住内存总线
锁总线 不同于屏蔽中断 锁总线能够防止其他处理器的影响 屏蔽中断 只能影响当前的处理器 对其他处理器没有影响
睡眠与唤醒
信号量
整型变量累计唤醒次数 正值 表示有一个或多个唤醒操作
down 和 up 操作 都为不可分割的原子操作
down 检查是否大于0 大于0 -1 为0 睡眠 检查数值 修改数值 睡眠 都为原子操作
若一个或多个进程在信号量上睡眠 不能完成一个先前的down 则随机挑选来完成down
使用信号量解决生产者-消费者问题
使用3个信号量
- full
- empty
- mutex
信号量也可以用于同步
互斥量
信号量的简化版本 仅仅适用于管理共享资源或一小段代码
数值0 表示解锁 其他的值表示加锁
管程
高级同步原语 由过程 变量和数据结构组成的集合 并且任意时刻 只能有一个活跃进程调用管程进程
如何让生产者在发现缓冲区满的时候阻塞?
引入条件变量 有wait 和 signal2个 且 sigal语句只能作为一个管程过程的最后一条语句 执行signal的进程必须立即退出管程
消息传递
进程间通信的方法使用2条原语 send receive 为系统调用
- 消息传递的要点
- 防止网络丢失
- 身份认证 确认是与文件服务器
- 正确接收与确认正确接收
- 用消息传递解决生产者–消费者问题
消费者发送消息 生产者发送包含实际数据的消息
对消息进行编址
- 为每个进程分配唯一一个地址
- 引进信箱这个数据结构 一个是有缓冲区的 一个是无缓冲区的
屏障
这个同步机制用于进程组 只有所有进程到达的时候才能够进行下一阶段
线程
为什么要有线程?
- 许多应用在同时发生着多种活动,分解为准并行的多个顺序线程可以让程序设计模型简单化
- 线程比进程更轻量级
- 性能方面 cpu密集型 不能得到性能的加强 但有大量计算和I/O处理的则会加快执行速度
进程模型的抽象使得不用考虑中断 定时器和上下文切换 有了多线程概念 就可以让并行实体共享同一个地址空间 和数据
像是3个线程程进行PDF的编辑 一个用于编辑 一个用于备份 一个用于监听 更加的方便与快捷
构造服务器的3个方法
模型 | 特性 |
---|---|
多线程 | 并行 阻塞系统调用 |
单线程进程 | 无并行 阻塞系统调用 |
有限状态机 | 并行性 非阻塞系统调用 中断 |
有限状态机 > 请求到来时 判断是否在高速缓存中能找到请求对应的吗 能则返回 不能则启动一个非阻塞的磁盘操作 服务器记录当前请求的状态 处理下一个事件 可能是新的请求 可能是磁盘操作结束的回答(一般以信号 或中断方式出现)
经典的线程模型
进程基于两个独立的概念:
- 资源分组处理
- 资源分组执行
进程可以理解为用某种方法把相关的资源集合于一起
每个进程存放了
- 程序正文和数据
- 全局变量
- 其他资源地址空间
- 打开文件
- 子进程
- 即将发生的报警
- 信号与信号处理程序
- 账户信息
每个线程存放了
- PC
- 寄存器
- 堆栈
- 状态
进程用于把资源集中到一起 线程则是在CPU上被调度执行的实体
用户空间实现线程
线程包放入用户空间
- 优点:可以在不支持线程的OS中使用 允许每个线程有自己的定制的调度算法 拥有较好的扩展性
- 缺点:
- 难实现阻塞系统调用 另一个相似的问题是(页面故障问题 并不是所有的程序都一次性放入内存 某个程序调用或跳转到一条不存在内存的指令会引起这个问题)
- 一个线程开始运行 其他线程不能运行 单独进程因无时钟中断 无法用轮转调用
OS内核空间存放进程表 用户空间都运行时系统存放 线程表
内核中实现进程
能够阻塞线程的调用 都以系统调用的形式实现 一个线程阻塞时 内核可以运行该线程所属的进程的其他线程 也可以运行其他进程中的线程
某个进程的线程引起页面故障 内核可以很快检查进程是否有任何其他可运行的线程 有的话 在等需要的页面读入过程 就可以让其他线程运行
- 缺点: 很难判断多线程进程创建新的进程时 创一个线程还是同等数量级的线程
混合实现
将一个内核级线程对应多个用户级进程
调度程序激活机制
模拟内核线程的功能
弹出式线程
传统处理服务请求是 请求来之前就准备线程
弹出式线程 是服务请求来之后 创建一个线程进行处理
试图将单线程转为多线程的问题是:有些库函数是不可重入的 不能在没调用完上一个的情况下 就调用下一个 (malloc也是类似的 ) 可以使用二进制标志位的包装器来解决上面的问题
对于在多线程中的信号问题 一个信号可能在不同线程表达的意思不同 也许是中断 亦或是捕捉信号
调度
OS为多道程序设计系统 有多个进程竞争CPU 多个进程处于就绪状态 如果只有一个CPU可用 则必须选择下一个要运行的进程
进行选择工作的则为调度程序
进程行为
- CPU密集型进程
- I/O密集型进程 如今要越来越偏重这个
何时调度
- 创建一个进程后 运行父进程还是子进程
- 进程退出
- 进程阻塞在I/O 或信号量
- I/O中断 根据硬件时钟的中断 进行调度
- 非抢占式调度算法 选一个进程直到进程自动释放CPU
- 抢占式调度算法 让其运行固定时段最大值 然后挂起 选择别的进程
调度算法分类
- 批处理 账单
- 交互式 防止一个进程霸占CPU
- 实时
调度算法的目标
基本目标: 公平 策略强制执行 平衡
批处理系统的调度 吞吐量 周转时间 CPU利用率
- 先来先服务
- 最短作业优先 非抢占式 只有当所有作业可运行情况下 才是最优化
- 最短剩余时间优先 上面的抢占式版本 运行时间需要掌握
交互式系统: 响应时间 均衡性
- 轮转调度 进程分配时间片 维护进程列表 时间片太短 进程切换过快 设置太长对交互的请求响应时间长
- 优先级调度 静态和动态赋予 随时间降低优先级 动态是使用完优先级的进程优先级越高
- 多级队列 为cpu密集型设置较长时间片比较耗时 进行递增的添加 不是1->1
- 最短进程优先
- 保证调度
- 彩票调度
- 公平分享调度
实时系统: 满足截止时间 可预测性
可静态(系统开始前做出调度,且要掌握所有信息) 可动态(运行过程中做出调度) 讲究实时性 迟到的应答还不如不来
- 硬实时: 绝对的截止时间
- 软实时: 不希望延时 但能容忍
按响应方式 又有周期性和非周期性事件
m Σ(i = 1)ci/pi <=1
pi: 事件i以周期pi发生
ci: 需要ci秒cpu时间处理
则是可调度的
策略和机制
上面的算法都是写死了的调度决策信息 无法接收用户进程的自定义调度决策信息 使用调度机制和调度策略分离 提供用户进程填写参数
线程调度
多个进程中有多个线程 调度处理取决于是用户级进程还是内核级进程
- 用户级进程
多道线程不存在时钟中断 可任意运行多长时间 线程用完了所属进程的时间片 内核切换到别的进程运行 缺陷是缺乏一个时钟将运行过长的线程中断
- 内核级进程
内核随意选线程 不考虑线程所属哪个进程
内核的线程切换比用户线程消耗资源要多 内核级进程要切换上下文等 修改内存镜像 清除高速缓存 但内核级线程时 线程阻塞在I/O上 不会像用户级线程样将整个进程挂起