2.1 进程
2.1.1 进程的概念
为什么需要进程?
-
为了提高计算机系统中各种资源的利用率, 现代操作系统广泛采用多道程序技术, 使多个程序同时在系统中存在并运行
-
一个进程应该包括
- 程序的代码
- 程序的数据
- PC中的值, 用来指示下一条将运行的指令
- 一组通用的寄存器的当前值, 堆, 栈;
- 一组系统资源 (如打开的文件)
-
程序是文本, 是语句的描述 (静态)
-
进程是运行中的程序, 含有上下文信息 (动态)
-
进程与程序
- 进程是动态的, 而程序是静态的
- 1 个程序可以对应多个进程
- 而 1 个进程只能对应 1 个程序
-
系统进程
-
用户进程
-
进程概念
- 进程是执行中的程序的抽象
-
关于进程概念有不同的说法
- 进程是程序的一次执行
- 进程是可以与其他计算并发执行和计算
- 进程是一个程序及其数据在处理器上顺序执行时发生的活动
- 进程是进程实体的一次活动
- 进程是支持程序运行的机制
-
进程定义
- 进程是具有一定功能的程序在一个数据集合上的运行过程,它是系统进行资源分配和调度管理的一个可并发执行的基本单位!
2.1.2 进程的特性
进程的特征
- 动态性: 进程的实质是程序的一次执行过程。进程是动态产生,动态消亡的,进程在其生命周期内,在三种基本状态之间转换。
- 并发性:任何进程都可以同其他进程一起向前推荐。
- 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
- 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可以预知的速度向前推进。
- 结构特征:为了控制和管理进程,系统为每个进程设立一个进程控制块 - PCB。系统中运行的实体通常由程序、数据和一个PCB三部分组成。
2.1.3 进程状态和转换
- 进程状态和转换
- 就绪(Ready)状态
- 进程一旦获得CPU就可以投入运行的状态
- 执行(Running)状态
- 进程获得CPU正在运行的状态
- 阻塞(Blocked)状态
- 进程由于等待资源或某个事件的发生而暂停执行的状态
- 就绪(Ready)状态
- 进程状态和转换
- 就绪 -> 执行: 被调度
- 执行 -> 阻塞: 等待资源、时间
- 阻塞 -> 就绪: 事件发生, 资源释放
- 执行 -> 就绪: 时间片用完
2.1.4 进程的组成
- 进程的组成
- 程序
- 数据
- PCB
- 进程控制块
- 一个进程只有一个PCB
- 是进程存在与否的唯一标记
- 描述信息
- 管理信息
- 进程控制块中的基本信息
- 进程标识信息
- 进程状态
- 进程特征
- 进程位置及大小信息
- 处理器线程保留区
- 进程资源清单
- 进程同步于通信机制
- 进程间联系
2.1.5 进程控制块和进程队列
进程控制块于进程队列
-
线性方式
-
链接方式
-
索引方式
2.1.6 进程控制
- 进程控制的主要任务
进程控制是对系统中所有进程从产生、存在到消亡的全过程实行有效的管理和控制。进程控制一般是由操作系统的内核来实现,内核在执行操作时,往往是通过执行各种原语来实现的
创建、撤销进程以及完成进程各状态之间的转换。由具有特定功能的原语完成。
-
进程创建原语
-
进程撤销原语
-
唤醒原语
-
原语: 操作系统内核中用于完成特定功能的一个过程,此过程在执行过程中呈现原子特征,不可中断。
进程创建过程
- 创建一个PCB
- 赋予一个统一进程标识符
- 为进程映象分配空间
- 初始化进程控制块
- 许多默认值 (如: 状态为 new)
- 设置相应的链接
- 如: 把新进添加到就绪队列的链表中
引起撤销的原因
- 正常结束
- 异常结束 (越界错、保护错、特权指令错、非法指令、运行超时、IO故障等)
- 外界干预(操作员干预、死锁、父进程请父进程终止)
进程阻塞原语
- 功能: 停止调用进程的执行, 变为等待
- 入口信息: 可省
进程的唤醒原语
- 功能: 唤醒某一处于等待队列当中的进
- 入口信息: 被唤醒进程的名字
- 引起唤醒的原因
- 系统服务由不满足到满足
- IO完成
- 新数据到达
- 进程提出新请求 (服务)
2.2 线程
2.2.1 线程引入和线程概念
- 线程引入
资源的拥有者:
给每个进程分配一虚拟地址空间, 保存进程映象, 控制一些资源 (文件, IO设备), 有状态、优先级、调度
调度单位:
进程是一个执行轨迹
以上两个属性构成进程并发执行的基础
对进程系统必须完成的操作:
- 创建进程
- 撤销进程
- 进程切换
缺点:时间空间开销大,限制并发度的提高
- 线程概念:线程 (Thread)是进程中实施调度和分派的基本单位
- 线程状态:运行状态、阻塞状态、就绪状态、终止状态
- 线程管理
- 线程创建
- 线程终止
- 线程等待
- 线程让权
- 线程与进程的关系
- 一个进程对应多个线程:一个线程只能在一个进程的地址空间内活动
- 资源分配给进程;同一个进程的线程共享资源
- CPU分配给线程
- 线程在执行过程中需要协作同步;不同进程则利用通信实现同步
2.2.2 线程的视线
- 用户线程与核心线程
- 用户级线程与核心级线程实现方式
2.2.3 线程池
- 线程池定义
- 将多个线程放在一个池中
- 线程池优势
- 服务更快
- 限定了线程数量
- 线程池设计原则
- CPU数量
- 物理内存容量
- 并发用户数量
2.2.4 线程优势
- 线程优势
- 响应度高
- 资源共享
- 经济
- 效能高
2.3 同步
2.3.1 进程同步和进程间通信
-
进程间相互关系
- 同步
- 所谓同步是指一组相互协同的进程,在完成同一人物,对某些资源进行操作时,为协调资源占用而相互等待、相互交换信息所产生的制约关系
- 互斥
- 所谓互斥是指并发进程间因相互竞争使用独占资源所产生的制约关系
- 通信
- 进程间交换信息成为通信
- 同步
-
临界资源与临界区
- 临界资源:就是一次只能允许一个进程使用
- 临界区:每个进程中访问或者使用临界资源的那段代码程序就叫做临界区
- 进程的通用结构
使用临界区的原则
- 空闲让进: 当无进程在互斥区时,任何有权使用互斥区的进程可以进入
- 忙则等待: 不允许两个以上的进程同时进入互斥区
- 有限等待:任何进入互斥区的要求应在有限的时间内得到满足
- 让权等待:处于等待状态的进程应放弃占用CPU,以使其他进程有机会得到CPU的使用权
2.3.2 互斥的实现方式
- 解决进程互斥进入临界区有两种方式
- 硬件
- 软件
- 硬件解决方式
- 禁止中断
- 专用机器指令
- 软件算法实现
- 进入区、退出区、剩余区
2.4 信号量
2.4.1 整形信号量
2.4.2 记录型信号量
- 为解决忙等待引入记录型信号量
- 记录型信号量定义:由两个成员组成的数据结构,其中一个成员是整型变量,表示该信号量的值;另一个是指向PCB的指针。当多个进程都要等待同一信号量时,它们就排成一个队列,由信号量的指针指示该队列的队首,进程控制块PCB是通过PCB自身所包含的指针项进行链接的,最后一个PCB的链接指针为0。
2.4.3 信号量的应用
2.4.4 经典同步问题
生产者消费者问题
- 注意三点:
- 每个进程先执行P操作,后执行V操作
- 同步信号量的PV操作要成对实现;应该在不同的进程中;
- 两个进程的P操作次序不能颠倒
读者写者问题
有两组并发进程:读者和写者,共享一组数据区
要求:
-
允许多个读者同时执行读操作
-
不允许读者、写者同时操作
-
不允许多个写者同时操作
-
如果读者来:
- 无读者、写者,新读者可以读
- 有写者等,但有其它读者正在读,则新读者也可读
- 有写者写,新读者等待
-
如果写者来:
- 无读者,新写者可以写
- 有读者,新写者等待
- 有其他写者,新写者等待
相关变量的定义:
伪代码:
理发师问题
场景:理发师店里有一位理发师和一把理发椅, 还有 N 张座椅供等候的顾客休息, 在没有顾客的时候, 理发师就会躺在理发椅上睡觉。因此,当有顾客来到理发店时,他必须先叫醒理发师。如果理发师正在理发时又有顾客来到,此时,如果有空椅子可做,他们会就会坐下来等;如果没有空椅子,就会离开。
问题:使用信号量和P、V原语写出理发师和顾客行为的程序描述?
分析:
- 理发师和每位顾客都分别是一个进程。
- 理发师开始工作时,先观察一下店内是否有顾客,如果没有他就在理发椅子上休息;如果有顾客,他就为等待时间最长的顾客服务,且顾客等待人数减 1.
- 每位顾客进程开始执行时,先看店内是否有空位,如果没有空位,就不等了,离开理发店;如果有空位,则排队,顾客等待人数加1;如果理发师在休息,则唤醒理发师工作。
解答:
引入三个信号量和一个控制变量
- 控制变量waiting来记录等候理发的顾客数量, 初始值为0
- 信号量 customers 用来记录等候理发的顾客数(不包括正在理发的顾客), 并用作阻塞理发师进程, 初值为0
- 信号量 barbers 用来记录正在等候顾客的理发师数, 并用作阻塞顾客进程, 初值为0
- 信号量 mutex 用于互斥, 初值为1
信号量和 P V原语的小结
- 把信号量视为一个加锁标志位, 其目的是为了实现对某个唯一的共享数据的互斥访问, 如数据库中的某个记录, 各个进程间的某个共享变量。该共享数据的取值与信号量本身的取值并没有什么直接的关系,信号量的作用仅仅是作为一个加锁标志位。其特征是信号量的初始值为1,然后在一个进程内部对它进行配对的PV操作
- 把信号量视为某种类型的共享资源的剩余个数,其目的是为了实现对这种类型的共享资源的访问,如各种IO设备。信号量的取值具有实际的意义,就等于空闲资源的个数。多个进程可以同时使用这种类型的资源,知道所有空闲资源均已用完。其特征是信号量的初始值为N(N>=1),然后再一个进程内部对他进行配对PV操作
- 把信号量作为进程间同步的工具, 利用它来设定两个进程在运行时的先后顺序。比如说,它可以是某个共享资源的当前个数,但是由一个进程负责生成该资源,而另一个进程负责消费该资源,由此引发了两个进程之间的先后顺序。其特征是信号量的初始值为N(N>=0),然后再一个进程里面用对它使用V原语,增加资源个数,而在另外一个进程里面对它使用P原语,减少资源个数,从而实现两个进程之间的同步关系
2.5 进程间通信
2.5.1 进程间通信的定义
- 进程通信是指进程之间可以直接以较高的速率传输较多的数据和消息的消息转换方式
- 三种基本进程通信方式
- 消息传递系统
- 共享存储器系统
- 管道通信系统
2.5.2 消息传递
- 假如需要通信的进程之间不存在直接访问的共享空间, 必须利用操作系统提供的通信类系统调用来实现进程间通信
- 发送原语:send(接受者进程名字,发送区首地址)。
- 接受原语:receive(发送者进程名字,接收区首地址)。
- 发送原语和接受原语是系统提供给用户进行实现进程通信的最基本的原语,也是构成一种具体通信系统的主要内容
- 一对一通信:发送消息的进程明确指定接受消息的进程,接受消息的进程也明白消息来自哪个进程,谓之一对一通信。
- 客户端和服务器之间通信:使用 Socket 编程
2.5.3 共享存储
- 把需要交换的信息发送到某一约定的存储区域,接受进程从该区域读取信息,从而实现2个或2个以上进程间的通信。这个通信方式称为共享内存。
- 共享存储区方式是目前系统进程通信中最高效的方式
- 该机制可以将内存中的一个区域连入多个进程的虚拟地址空间
- 涉及共享区的系统调用通常有创建、附接、断接、状态查询。
- 创建 shmget(key,size,flag)
- 附接 shmat(shmid,addr,flag)
- 断接shmdt(viraddr)
- 状态查询shmctl(shmid,cmd,buff)
- 共享区存储的好处:为通信进程提供直接通信的手段, 使得通信进程通过写(发送消息)、读(接受消息)对方的虚空间完成相互通信, 通信的效率很高.
2.5.4 管道通信
- 管道是一种信息缓冲机构, 它用于连接发送进程和接受进程, 以实现它们之间的数据通信. 管道不同一般的数据缓冲, 它以先进先出 的方式组织数据的传输
- 管道通信有点: 交换的信息量大, 消息保存期长
- 管道通信缺点: IO操作的次数较多, 同步和管理机构也比较复杂
- 管道机制由管道文件创建, 管道文件的读写以及读写操作时的同步机构3部分程序组成。
- 管道通常用于有共同祖先的进程间的消息交换
2.6 进程调度
2.6.1 调度概念的引入
- 处理机管理是影响操作系统的主要功能之一. 处理机管理的实现策略决定了操作系统的类型, 其算法好坏直接影响整个系统的性能
- 所谓进程调度, 就是通过某种规则或算法从就绪队列中选出一个进程投入运行
- 调度是一个基本的操作系统功能. CPU调度是操作系统设计的核心问题
2.6.2 CPU 调度程序
- CPU调度程序任务
调度程序的任务是负责CPU上切换进程. 当正运行的进程应该从CPU移除 (改变到等待或阻塞状态) 时, 从处于就绪状态的多进程中选择一个不同的进程. CPU被分配给被选择的进程, 新的进程的状态从就绪改变为运行
- 调度策略
调度策略确定进程何时从CPU移除并且应当接着将CPU分配给某个等待的进程.
CPU调度策略可在如下四种情况下发生:
1. 当一个进程从运行状态切换到阻塞状态
2. 当一个进程从运行状态切换到就绪状态
3. 当一个进程从阻塞状态切换到就绪状态
4. 当一个进程终止
- 非抢占调度方案, 一旦CPU被分配给一个进程, 该进程就被执行到底, 一直将CPU使用到进程结束或切换到等待状态在释放.
- 可抢占调度方案, 运行程序根据某种策略中止当前进程的执行, 将其一入就绪队列, 并选择另外一个进程投入运行.
- CPU调度程序主要模块.
- 三个主要部分: 排队程序、分配程序、上下文切换程序
(1) 排队程序
当一个进程改变到阻塞状态时, 更新进程描述符并且队列程序通过指针关联进程描述符, 该进程进入等待CPU的进程列表 (称为等待列表)
(2) 分配程序
分配程序用来将CPU的控制交给短期调度程序选择的进程
这部分程序要包括以下功能模块:
- 切换上下文
- 切换到用户模式
- 跳转到用户程序的合适为止以重新启动用户这个程序
(3) 上下文切换程序
当调度程序切换 CPU, 从执行一个进程到执行另一个进程时, 上下文切换程序保存从CPU移除进程的所有处理器寄存器的内容到进程的PCB中
2.6.3 调度准则
-
影响调度算法选择的主要因素
(1) 设计目标
(2) 公平性
(3) 均衡性
(4) 综合平衡
(5) 基于相对优先级
-
调度性能评价
① CPU利用率
② 吞吐量
③ 周转时间
④ 就绪等待时间
⑤ 响应时间
最常见的CPU调度策略
- 先来先服务调度算法
- 短作业优先调度算法
- 优先级调度算法
- 轮转调度算法
- 多级队列调度算法
- 先来先服务调度算法
先来先服务 先请求CPU的进程先获得CPU
- 最简单的CPU调度算法
- 对CPU繁忙型进程比较有利
- 非抢占性的
- 先来先服务调度算法对CPU繁忙型进程比较有利, 而对于 IO繁忙型进程则不利
- 是非抢占性的. 一旦CPU被分配给一个进程, 该进程持有 CPU 直到它释放 CPU (通过终止或请求IO)
- 短作业优先调度算法
该算法将每个进程与下一个CPU占用时间长度相关联, 当CPU可用时, 它将优先被分配给具有最短后续CPU占用的进程, 这种算法假定事先知道了每个进程下次运行的CPU占用长度.
- SJF 调度算法为指定的进程组给出了最小的平均等待时间
- 短作业优先也是非抢占性的
- 最短剩余时间优先调度(SRTF), 抢占性短作业优先算法
- 优先级调度算法
- 优先级调度算法是从就绪队列中选出优先级别最高的进程, 让它占用CPU运行
- 静态优先级: 静态优先级调度算法是指创建进程时就确定下来的, 而且在进程整个运行期间其优先级是维持不变的
- 动态优先级: 动态优先级是随着进程的推进而不断变化的
-
静态优先级调度算法
- 优先级调度算法易于实现, 系统的开销也小
- “饥饿” 现象, 某些优先级别比较低的进程可能无线等待CPU调度, 如果高级别的进程很多, 形成一个稳定的进程队列, 可能使低级别进程任何时候也得不到CPU资源, 会 “饿死了”
-
动态优先级调度算法
- 为了解决饥饿现象, 而产生的一种解决方案
- 方法: 老化处理
- 轮转 (Round-Robin, RR) 调度算法
- RR 算法定义了一个小的时间单元, 被称为时间片, 一个时间片通常在 10 ms 到 100 ms之间
- 把就绪队列作为循环队列对待. CPU 调度程序环绕这个就绪队列, 将 CPU 分配到每个进程, 每隔一个时间片转换一次
- 将所有就绪进程按照先入先出的原则排成队列, 新来的进程加到就绪队列的末尾, 但是轮转调度添加了进程间的抢占切换
- 轮转调度算法的特点
- RR 策略的平均等待时长通常会相当长
- 适用于分时系统
- 性能依赖于时间片的大小
- 时间片的大小通常由以下几个因素决定
(1) 时间片大小和上下文切换有关
(2) 系统的响应时间
(3) 进程周转时间
(4) 时间片大小和CPU主频有关
2.7 死锁
2.7.1 死锁的背景
- 资源共享问题产生
- 两个或多个进程无限期地等待永远都不会发生的条件, 系统处在停滞状态
- 死锁不仅发生在硬件资源上, 也有可能发生在软件资源的使用上
2.7.2 产生死锁的必要条件
如果四个条件同时成立, 那么死锁就会发生
- 互斥条件
- 持久并等待条件
- 不可抢占条件
- 循环等待条件
2.7.3 资源使用方式
- 在通常操作方式下, 进程只能通过如下的顺序使用资源
- 请求: 如果请求不能立刻得到允许, 那么发出请求的进程必须等待, 直到它可以获得该资源
- 使用: 进程可以对资源进行操作
- 释放: 进程释放它以前请求且分到的资源
2.7.4 死锁的处理方法
死锁的处理方法
- 预防死锁
- 避免死锁
- 检测与解除死锁
- 将死锁忽略不计, 认为死锁不可能在系统内发生
2.7.5 预防死锁
- 如果我们能够确保产生死锁的四个必要条件有一个不成立, 就可以预防死锁.
-
- 互斥条件
-
- 持有并等待条件
-
- 不可抢占条件
-
- 循环等待条件
-
- 资源一次性分配; (破坏请求和保持条件)
- 可剥夺资源; 即当某进程新的资源未满足时, 释放已占有的资源或剥夺等待进程的资源 (破坏不可剥夺条件)
- 资源有序分配法 ; 做法: 系统给每类资源赋予一个编号, 每一个进程按编号递增的顺序请求资源, 释放则相反(破坏环路等待条件)
2.7.6 避免死锁
- 死锁避免定义: 在系统运行过程中, 对进程发出的每一个系统能够满足的资源申请进行动态检测, 并根据检查结果决定是否分配资源, 若分配后系统可能发生死锁, 则不予分配, 否则预分配
2. 资源分配图算法
- 银行家算法
2.7.7 死锁的检测与解除
- 死锁的检测
如果系统没有采用死锁预防或死锁避免算法, 那么就有可能发生死锁, 在这种环境下, 系统必须提供:
- 用于检测系统状态以确定是否发生死锁的算法
- 从死锁中恢复的算法