目录儿
- 四、进程管理
- 4.1 什么是进程
- 4.1.1 进程的结构
- 4.1.2 进程的特征
- 4.1.3 进程与线程
- 4.1.4 线程的实现方式
- 用户级线程
- 内核支持线程
- 组合线程的调度
- 4.2 进程是怎么运行的
- 4.2.1 进程状态
- 4.2.2 进程控制
- 4.2.2.1 原语的概念
- 4.2.2.2 挂起与激活
- 4.2.3 进程调度
- 4.2.3.1 调度层次
- 4.2.3.2 调度方式
- 4.2.3.3 调度时机
- 4.2.3.4 调度过程
- 4.2.3.5 调度算法
- 4.3 进程之间是怎么协作的
- 4.3.1 进程通信
- 4.3.1.1 共享存储
- 4.3.1.2 消息传递
- 4.3.1.3 管道通信
- 4.3.2 进程同步
- 4.3.2.1 互斥访问共享资源
- 软件实现
- 硬件实现
- 4.3.2.2 直接相互制约关系(同步)
- 信号量(Semaphore)机制
- 4.3.2.3 管程
- 4.4 如何处理死锁问题
- 4.4.1死锁的概念
- 4.4.2 预防死锁
- 4.4.3 死锁的检测
- 4.4.4 如何解除死锁
四、进程管理
4.1 什么是进程
进程,是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行 资源分配和调用 的一个独立单位。
- 进程是程序的一次执行(加载进内存中执行)。
- 进程是一个程序及其数据在处理机上顺序执行时所发生的 活动。
- 进程是程序在一个 数据集合 上运行的过程。
- 进程是系统进行 资源分配的最小单位(或者说基本单位)。
4.1.1 进程的结构
进程的结构分三部分:
- 控制块(PCB Process Control Block ):操作系统中最重要的记录型数据结构,包含以下4方面信息。
- 进程标识符信息:进程标识符用于惟一地标识一个进程。一个进程,通常有以下两个标识符:外部标识符,内部标识符。
- 处理机状态信息:处理机状态信息主要是由处理机各种寄存器中的内容所组成。
- 进程调度信息:与进程调度和进程对换有关的信息,包括:进程状态、进程优先级、进程调度所需要的其他信息、事件。
- 进程控制信息:包括 程序和数据的地址、进程同步和通信机制、资源清单、链接指针。
- 数据段:存放原始数据、中间数据。
- 程序段:存放文本区域,可被多个进程共享(同一应用程序多开共 用一份程序指令)。
4.1.2 进程的特征
- 动态性:由创建而生,由撤销而亡。
- 并发性:多个进程同时运行。
- 独立性:独立资源分配。
- 异步性:进程间互相独立,互不干扰。
4.1.3 进程与线程
- Thread,进程的轻型实体,也叫轻量级进程,是系一列活动按事先设定好的顺序依次执行的过程,是一系列指令的集合。
- 是一条执行路径,不能单独存在,必须包含在进程中。
- 线程是操作系统中 运算调度的最小单位 (基本单位)。
为什么引入线程?
为了提高操作系统的并发性,为了充分利用处理机的算力,原来的做法是把一个多任务进程分成多个小进程,但是因为多个进程的创建和撤销增加了系统开销导致开销大(频繁进出内存)、数据交互的不安全性等原因,提出了线程的概念:在一个进程中划分多个轻量级进程(线程),这种轻量级进程不参与资源分配,共享进程的资源。在节省了系统开销的同时提升了并发性,因此被广泛应用。
目前最新或者说未来可能广泛应用的概念是纤程,是线程的进一步细分。
线程属性:
- 轻型实体。
- 独立调度和分派的基本单位。
- 可并发执行。
- 共享进程资源。
4.1.4 线程的实现方式
线程与进程一样都有自己的控制块,但不同级别的线程,控制块在不同空间,由不同的调度器负责调度分配。每个用户线程都必须先与某个内核线程绑定,然后内核线程再由操作系统调度器以时间片轮转策略指派至某个处理机进行运算处理。
用户级线程(ULT User Level Thread ):在用户空间实现,控制块在用户空间,由线程库调度器负责调度分配。
内核级线程(KLT Kernel Level Thread ):在内核空间实现,控制块在内核空间,由操作系统调度器负责调度分配。
内核级线程又称内核支持线程。
用户级线程
- 运行和控制块都在用户空间,线程调度由所属进程控制(线程库调度器)。
- 对操作系统(内核线程)不可见,不涉及系统调用。
- 不需要用户态/核心态切换,线程间的切换速度快。
- 同一时刻进程内只有一个线程被调度,只有一个处理机在运算。
- 内核线程与进程绑定而非进程内的某个具体线程。
内核级线程是由操作系统分配时间片,操作系统给进程分配一个时间片,进程直接把这个时间片分配给内核级线程,相当于操作系统直接给线程分配时间片。
内核支持线程
- 控制块在内核空间,线程调度由操作系统控制
- 对操作系统(内核线程)可见,涉及系统调用。
- 多处理器系统中,多个处理器内核能够并行执行同一进程内的多个线程
- 内核线程与进程内的某个具体线程绑定。
组合线程的调度
例:在进程A中用户线程1、2为用户级线程,用户线程3为内核支持线程,其中内核线程1负责处理进程A中的用户线程,由库调度器调度决定某一时刻处理用户线程1、2中的哪一个;内核线程3负责处理用户线程3(它需要系统调用,属于内核支持线程),整个处理过程中它不接受库调度器的调度,也就是整个处理过程它会专心地处理用户线程3。
无论是用户级线程还是内核支持线程,内核与处理机的调度都是一样的,都是由操作系统采用时间片轮转策略完成调度。二者的区别就是进程内的线程与内核线程是否是绑定关系。
4.2 进程是怎么运行的
4.2.1 进程状态
1.三种基本状态
- 就绪(Ready):等待CPU调度
- 执行(Running):CPU正在调度
- 阻塞(Blocked):暂停调度,把进程放入阻塞队列,加载资源
创建和终止状态
- 创建(New):加载程序文件,给程序分配资源
- 终止(Terminated):自然终止、异常终止、干预终止,释放掉占用的资源。
4.2.2 进程控制
即OS对进程实现有效的管理,包括创建新进程、撤销已有进程、挂起、阻塞和唤醒、进程
切换等多种操作。OS通过 原语 (Primitive)操作实现进程控制。
4.2.2.1 原语的概念
由若干条指令组成,完成特定的功能,是一种原子操作(Atomic Operation)
- 原子操作,要么全做,要么全不做,执行过程不会被中断
- 在管态/系统态/内核态下执行,常驻内存
- 是内核三大支撑功能(中断处理/时钟管理/原语操作)之一
4.2.2.2 挂起与激活
挂起就是暂停进程运行,把进程从内存移到外存中。
外存是专门从磁盘划出来的一块存储区域,用作与内存交换作业。
挂起只能从阻塞或就绪状态发起,执行状态的进程不能被挂起。
从阻塞状态挂起的进程称为 静止阻塞 。
从就绪状态挂起的进程称为 静止就绪 。
- 静止就绪:放外存,不参与调度排队
- 静止阻塞:放外存,不参与调度排队,等待事件
挂起和激活是一个事件(动作),不是一个状态,且都是通过原语实现。
4.2.3 进程调度
进程的调度实际上是处理机调度,进程是被调度。
处理机调度是根据一定的算法和原则将处理机资源进行重新分配的过程。
◆ 前提:作业/进程数远远大于处理机数
◆ 目的:提高资源利用率,减少处理机空闲时间
◆ 调度程序:一方面要满足特定系统用户的需求(快速响应),另一方面要考虑系统整体效
率(系统平均周转时间)和调度算法本身的开销
4.2.3.1 调度层次
- 高级调度/作业调度
- 把磁盘程序加载进内存,生成进程。
- 只调入(创建)一次,调出(撤销)一次
- 中级调度/内存调度
- 将进程调至外存,条件合适再调入内存
- 在内、外存对换区进行进程对换
- 低级调度/进程调度
- 从就绪队列选取进程分配给处理机
- 最基本的调度,频率非常高(相当于一个时间片的时间)
4.2.3.2 调度方式
- 剥夺式/抢占式调度
- 立即暂停当前进程
- 分配处理机给另一个进程
- 原则:优先权/短进程优先/时间片原则
- 非剥夺/非抢占调度
- 若有进程请求执行
- 等待直到当前进程完成或阻塞
- 缺点:适用于批处理系统,不适用分时/实时系统
4.2.3.3 调度时机
- 进程运行完毕
- 进程时间片用完
- 进程要求 I/O 操作
- 执行某种原语操作
- 高优先级进程申请运行(剥夺式调度)
4.2.3.4 调度过程
大体概括为四步:
1.保存镜像:记录进程现场信息
2.调度算法:确定分配处理机的原则
3.进程切换:分配处理机给其它进程
4.处理机回收:从进程收回处理机
调度算法指标:
- CPU利用率 ↑ (忙碌时间 / 总时间)
- 系统吞吐量 ↑ (完成作业数 / 总时间)
- 周转时间 ↓ (作业完成时间提交时间)
- 带权周转时间 ↓ (周转时间 / 实际运行时间)
- 等待时间 ↓ (作业等待处理机调度时间平均值)
- 响应时间 ↓ (提交请求到首次响应间隔)
4.2.3.5 调度算法
常见调度算法:
先来先服务 FCFS
算法内容:调度作业/就绪队列中最先入队者,等待操作完成或阻塞
算法原则:按作业/进程到达顺序服务(执行)
调度方式:非抢占式调度
适用场景:作业进程调度
优缺点:
◆有利于CPU繁忙型作业,充分利用CPU资源
◆不利于I/O繁忙型作业,操作耗时,其它饥饿
短作业优先SJF
算法内容:所需服务时间最短的作业进程优先服务(执行)
算法原则:追求最少的平均(带权)周转时间
调度方式:SJF/SPF非抢占式
适用场景:作业进程调度
优缺点:
◆平均等待/周转时间最少
◆长作业周转时间会增加或饥饿
◆估计时间不准确,不能保证紧迫任务及时处理
高响应比优先调度HRRN
算法内容:结合FCFS和SJF,综合考虑等待时间和服务时间计算响应比,高的优先调度
算法原则:综合考虑作业进程的等待时间和服务时间
调度方式:非抢占式
适用场景:作业进程调度
响应比计算:
◆响应比=(等待时间+服务时间)/服务时间,≥1
◆只有当前进程放弃执行权(完成/阻塞)时,重新计算所有进程响应比
◆长作业等待越久响应比越高,更容易获得处理机
优先级调度PSA
算法内容:又叫优先权调度,按作业/进程的优先级(紧迫程度)进行调度
算法原则:优先级最高(最紧迫)的作业/进程先调度
调度方式:抢占/非抢占式
适用场景:作业进程调度
优先级设置原侧:
◆静态/动态优先级
◆系统 > 用户;交互型 > 非交互型;I/O型 > 计算型
◆低优先级进程可能会产生“饥饿”
时间片轮转调度RR⭐
算法内容:按进程到达就绪队列的顺序,轮流分配一个时间片去执行,时间用完则剥夺
算法原则:公平、轮流为每个进程服务,进程在一定时间内都能得到响应
调度方式:抢占式,由时钟中断确定时间到
适用场景:进程调度
优缺点:
◆公平,响应快,适用于分时系统
◆时间片决定因素:系统响应时间、就绪队列进程数量、系统处理能力
◆时间片太大,相当于FCFS; 太小,处理机切换频繁,开销增大
每个进程服务的时间一般不相等。
多级反馈队列调度MFQ
算法内容:设置多个按优先级排序的就绪队列,优先级从高到底,时间片从小到大,新进程采用队列降级法,进入第一级队列,按FCFS分时间片,没有执行完,移到第二级,第三级…,前面队列不为空,不执行后续队列进程
算法原则:集前几种算法优点,相当于PSA+RR
调度方式:抢占式
适用场景:进程调度
优缺点:
◆对各类型相对公平,快速响应;
◆终端型作业用户,短作业优先;
◆批处理作业用户,周转时间短;
◆长批处理作业用户,在前几个队列部分执行;
4.3 进程之间是怎么协作的
4.3.1 进程通信
进程是资源分配的基本单位,各进程内存空间彼此独立。一个进程不能随意访问其它进程的地址空间,但是进程之间常常有信息交换的需求。
进程通信的三种方式:
共享存储(Shared-Memory)
消息传递(Message-Passing)
管道通信(Pipe)
4.3.1.1 共享存储
共享存储有两种方式
- 共享数据结构
- 多个进程共用某个数据结构(由操作系统提供并控制)
- 由用户(程序员)负责同步处理
- 属于低级通信,可以传递少量数据,效率低
- 共享存储区
- 多个进程共用内存中的一块存储区域
- 由进程自己控制数据的形式
- 属于高级通信,可以传递大量数据,效率高
缺点:数据收发双方不可见,存在安全隐患。
4.3.1.2 消息传递
- 直接通信:点到点发送
- 发送和接收时指明双方进程的ID
- 每个进程维护一个消息缓冲队列
- 间接通信:广播信箱
- 以信箱为媒介,作为中间实体(中间件)
- 发进程将消息发送到信箱,收进程从信箱读取
- 可以广播,容易建立双向通信链
消息传递是通过操作系统提供的原语操作的。
4.3.1.3 管道通信
管道实际上是一个文件,用于连接读/写进程的共享文件/pipe文件,本质是内存中固定大小的缓冲区。
- 特点:是一种半双工通信
- 同一时段只能单向通信,双工通信需要两个管道
- 以先进先出(FIFO)方式组织数据传输
- 通过系统调用read()/write()函数进行读写操作
- 管道(缓冲区)未满不读、已满不写、未空不写、一空不读、读后删除
- 管道(缓冲区)大小由操作系统决定,固定大小。
4.3.2 进程同步
协调进程间的相互制约关系,使它们按照预期的方式执行的过程就是进程同步,分为互斥和同步两种制约形式。
- 进程同步前提:
- 进程是并发执行的,进程间存在着相互制约关系
- 并发的进程对系统共享资源进行竞争
- 进程通信,过程中相互发送的信号称为消息或事件
- 两种制约形式
- 间接相互制约关系(互斥):进程排他性地访问共享资源。
- 直接相互制约关系(同步):进程间的合作,比如管道通信。
4.3.2.1 互斥访问共享资源
互斥访问共享资源过程:
1.进入区:尝试进入临界区,成功则加锁(Iock)
2.访问区:访问共享资源
3.退出区:解锁(unlock),然后唤醒其它阻塞进程提示临界区已解锁可访问
4.剩余区:其它代码
共享资源也称临界资源、临界区
访问原则:
1.空闲让进:临界区空闲,允许一个进程进入
2.忙则等待:临界区已有进程,其它进程等待(阻塞状态)
3.有限等待:处于等待的进程,等待时间有上限
4.让权等待:进程在等待时应让出CPU执行权,防止“忙等待”
软件实现
-
单标志法
两个进程交替访问临界区,分别以进程id(此处是0、1)作为进入标志来实现互斥访问。缺点:当其中一个进程不再访问临界区时,另一个也没法访问,违背“空闲让进”原则,非常尴尬。
-
双标志先检查
用一个标志(flag)数组存储每个进程的进入标志,尝试进入之前先检查别的进程的进入标志,如果为true,则等待,为false,则进入访问并把自己的访问标志改为true,表示正在访问资源阻止其他进程的访问。缺点:判断和修改标志不是一个原子操作,在修改进入标志之前可能被其他进程先行判断,并进入访问。违背“忙则等待”原则。
-
双标志后检查
与双标志先检查相似,不同的是在尝试进入的时候先修改自己的进入标志再检查是否可访问,且多个进程都把自己的进入标志修改为true,那么大家都在等待,都进不去。缺点:不确定是否可访问时先上锁,违背“空闲让进”和“有限等待”原则。
-
皮特森算法
优点:综合了双标志和单标志,可以实现较为可靠的互斥访问。
缺点:违背“让权等待”,会发生忙等(自旋锁造成一定的性能浪费)。
硬件实现
-
中断屏蔽:关/开中断
- 禁止一切中断,CPU执行完临界区之前不会切换
- 关中断可能会被滥用
- 关中断时间长影响效率
- 不适用于多处理机,无法防止其它处理机调度其它进程访问临界区
- 只适用于内核进程(该指令运行在内核态)
-
Test-And-Set(TS指令/TSL指令)
- 读出标志并设置为true,返回旧值,原子操作
- 也被称作TSL指令(Test-And-Set-Lock)
- 违背“让权等待”,会发生忙等
-
Swap指令(EXCHANGE, XCHG指令)
- 交换两个变量的值,原子操作
- 违背“让权等待”原则
4.3.2.2 直接相互制约关系(同步)
信号量(Semaphore)机制
-
整型型号量
信号量相当于资源数,使用的依然是自旋忙等、原语。PV操作:
P操作:wait原语,进程等待
V操作:signall原语,唤醒等待进程缺点:因为自旋忙等在等待的过程中依然占据着处理机的执行权,会发生忙等,违背了“让权等待”原则。
-
记录型信号量
客服了整型信号量的缺点,进程进入阻塞状态,不会忙等。
在实际生成中,唤醒是唤醒整个等待队列里的进程。
4.3.2.3 管程
管程(Monitor、监视器),管理同步进程,即 用于实现进程同步 的工具。是由代表共享资源的数据结构和一组过程(进行PV操作的函数(信号量))组成(封装)的管理程序。
-
管程的组成
- 管程名称
- 管程内部的共享数据结构
- 对该数据结构操作的一组过程(函数)
- 管程内共享数据的初始化语句
-
管程的基本特性:
- 是一个模块化的基本程序单位,可以单独编译(是一个独立程序)
- 是一种抽象数据类型,包含数据和操作
- 信息掩蔽,共享数据只能被管程内的过程访问
-
条件变量/条件对象
- 进入管程的进程可能由于条件不满足而阻塞
- 此时进程应释放管程以便其它进程调用管程
- 进程被阻塞的条件(原因)有多个,移入不同的条件队列
- 进程被移入条件队列后,应释放管程
4.4 如何处理死锁问题
4.4.1死锁的概念
定义:
多个进程由于竞争资源而造成的阻塞现象,若无外力作用,这些进程将无法继续推进。
相似概念:饥饿
等待时间过长以至于给进程推进和响应带来明显影响,“饿而不死”。
死锁产生的原因
① 系统资源的竞争。
② 进程推进顺序非法。
死锁产生的必要条件
① 互斥条件:共享资源的排他性访问
② 不剥夺条件:访问时该共享资源不会被剥夺
③ 请求并保持条件:保持当前资源时请求另一个资源
④ 循环等待条件:存在共享资源的循环等待链
4.4.2 预防死锁
破坏死锁产生的四个必要条件之一即可。
① 破坏互斥条件
■ 将只能互斥访问的资源改为同时共享访问(用进程队列的形式访问)
■ 将独占锁改为共享锁(实际上是对队列上锁,而不是某个单独的进程)
■ 不是所有资源都能改成可共享的
② 破坏不剥夺条件
■ 请求新资源无法满足时必须释放已有资源
■ 由OS协助强制剥夺某进程持有的资源
■ 实现复杂,代价高
■ 此操作过多导致原进程任务无法推进
③ 破坏请求并保持条件
■ 进程开始运行时一次性申请所需资源(产生资源浪费、进程饥饿)
■ 阶段性请求和释放资源
④ 破坏循环等待条件
■ 对所有资源现行排序,按序号请求资源
■ 请求时先低再高
■ 释放时先高再低
■ 对资源的编号应相对稳定,限制了新设备增加
■ 进程使用资源的顺序可能与系统编号顺序不同
■ 限制了用户编程
4.4.3 死锁的检测
需要一种数据结构,保存有关资源的请求和分配信息
提供一种算法,利用这些信息检测是否形成了死锁
4.4.4 如何解除死锁
- 资源剥夺
- ① 挂起死锁进程
- ② 剥夺其资源
- ③ 将资源分配给其他(死锁)进程
- 撤销进程(直接Shutdown杀掉)
- 进程回退
- 回退到足以避免死锁的地步(需要记录进程历史信息,设置还原点)
资料来源:
- 哔哩哔哩 马士兵-小森 清华大牛耗时1000分钟把计算机底层知识 | 计算机组成原理 | 操作系统 | 数…
https://www.bilibili.com/video/BV1qe4y1T7t3?p=37&spm_id_from=pageDriver&vd_source=c2e346bb74807b3fde142d31e57292ce
- 知乎 玩转Linux内核 线程的3种实现方式(内核级,用户级和混合型)
https://zhuanlan.zhihu.com/p/453386792