操作系统四大特征:并发性,共享性,虚拟技术,异步性,都是基于进程形成的,进程至关重要。本节介绍进程的相关问题。主要包括基本概念,进程控制,进程同步,经典的同步,进程通信,线程。
进程的基本概念
基本概念
- 程序的并发执行:不存在完全的前驱关系。
- 并发执行程序的特征:间断性,失去封闭性,不可再现性。
- 进程的特征:
- 结构特征:进程控制块 PCB,Process Control Block, 由程序段、相关数据段和 PCB 三部分组成。(创建和撤销进程,一般指对 PCB 的创建和撤销)
- 动态性:进程具有一定的生命期,一般指进程实体的一次执行过程。
- 并发性:多个进程实体同存在与内存,在一段时间内同时运行。
- 独立性:能独立运行,独立分配资源和独立接受调度。
- 异步性:进程实体异步方式运行。
- 进程定义:“进程是进程实体的运行状态,是系统进行资源分配和调度的一个独立单位”。
- 进程的基本状态:
- 就绪状态:进程分配到除 CPU 外的必要资源。往往进入就绪队列。
- 执行状态:以获得 CPU 正在执行。
- 阻塞状态:由于某些事件(I/O 请求,申请缓冲区)暂时无法继续执行,放弃 CPU 进入暂停状态(也称为阻塞状态,等待/封锁状态)。
- 挂起状态:在上述三种状态中,增加挂起状态,使得程序出现活动阻塞,禁止阻塞,活动就绪,禁止就绪。主要导致挂起的原因:终端用户请求,父进程请求,负荷调节需要,操作系统需要。
- 创建和终止状态:创建:创建 PCB,并把进程转入就绪状态插入队列(在未获得必须资源未进入队列时则时创建状态)。终止:等待操作系统善后,PCB 清零空间回收(处在终止状态的进程的信息仍然可被收集)。

进程控制块
- 进程控制块:为描述和控制进程定义的一个数据结构,进程实体的一部分,记录操作系统所需的、用于描述当前进程情况以及控制进程运行的全部信息。使在多道程序环境下不能运行的程序称为一个可独立运行地与其他进程并发执行的基本单位。PCB 是进程的唯一标志,OS 通过 PCB 来对并发执行的进程进行管理。PCB 经常改写,所以常驻内存。
- 主要包含的信息:
- 进程标识符:内部标识符(系统使用);外部标识符(由创建者提供,为用户/进程访问该进程时使用,例如父进程标识/子进程标识,用户标识等)
- 处理机状态:主要由各种寄存器内容组成,在中断时这些信息必须存储在 PCB 中,以便重新执行。主要包括的寄存器:通用寄存器,指令计数器,程序状态字 PSW,用户栈指针。
- 进程调度信息:进程状态,进程优先级,进程调度所需的其他信息,事件(阻塞原因)。
- 进程控制信息:程序和数据的地址(内/外存(首)地址),进程同步和通信机制的信息,资源清单,链接指针(下一个执行的 PCB 首地址)。
- 进程控制块的组织方式:链接方式(不同指针指向不同 PCB,PCB 的指针指向下一块同状态的 PCB)和索引方式(建表存储不同状态的进程 PCB)。
进程控制
- 进程控制是进程管理的最基本的功能,主要负责进程状态转换。采用原语/原子操作,原语不可分割,常驻内存。
进程的创建
- 进程图:描述进程的父子关系。父指向子的有向树。
- 引起创建的原因:用户登录,作业调度,提供服务和应用请求。
- 进程创建:
- 申请空白 PCB
- 为新进程分配资源
- 初始化 PCB:初始化标识信息,初始化处理器状态,初始化处理器控制信息。
- 进入就绪队列
进程终止:
- 引起终止的原因:正常结束,异常结束,外界干预
- 终止过程:
- 检索进程 PCB
- 终止进程执行,设置调度标识为真
- 终止子进程
- 终止拥有的全部资源,归还资源
- 队列移出。
进程阻塞与唤醒
- 引起阻塞的原因:请求系统服务,启动了某种操作,新数据尚未到达,无新工作可做。(等待被请求,提出请求,请求执行,三种情况都可能被阻塞,或者是无事可做)
- 通过阻塞原语 Block 阻塞:停止执行,修改现行状态,插入 PCB 阻塞队列,切换处理器状态(保存现有处理器状态到 PCB,从新进程 PCB 调入处理器状态)。进程阻塞是进程自身的一种行为。(没有回收资源)
- 唤醒:唤醒原语:移出阻塞队列,PCB 现行状态从阻塞改变为就绪,加入就绪队列。
进程挂起与激活
- 挂起流程:检查进程状态,改变进程状态(就绪或执行转为静止就绪,阻塞改为静止阻塞),复制 PCB 信息到指定内存区域(便于用户和父进程考察)。若被挂起进程本是被执行的,因转向调度程序重新调度。
- 激活过程;内存有足够空间,则从外存调入内存,改变现行经常状态。
进程同步
进程同步:在多程序并发执行过程中,协调进程执行次序,保证资源有效正确的使用和各进程的相互合作,从而使得程序的执行具有可再现性。
进程同步的基本概念
- 两种制约关系:间接制约(公用临界资源),直接制约(直接的输入输出关系,常利用缓冲区)。
- 临界资源:不允许不同进程同时访问,互斥的。
- 临界区:访问临界资源的那段代码。常在临界区前后这只进入区和退出区来处理临界资源互斥问题。
- 同步机制的规则:空闲让进,忙则等待,有限等待,让权等待。
信号量机制
- 信号量机制建立在原子操作基础上的,wait(S)提出申请使用临界资源和 signal(S)释放临界资源。
- 整形信号量:用一个整型量记录临界资源使用情况。
- 记录型信号量:满足让权等待,有列表记录需求使用临界区的进程。
- AND 型信号量:在记录型信号量基础上,考虑多种临界资源同时需要的情况,则采用&&运算判断每种资源均有,才能操作。
- 信号量集:资源管理上可一次分配和释放多个资源。
信号量的应用
- 使用了信号量实现进程互斥:wait(mutex)和 signal(mutex)成对出现,保证资源使用互斥和资源能被正常释放。
- 利用信号量实现前趋关系:在优先进程在执行完后进行 signal(mutex),而后续进程则在执行前 wait(mutex)。可实现大量复杂的前趋关系。
管程机制
为避免大量的同步操作分散在各个进程种,而产生的新进程同步工具管程。
- 管程定义:由代表共享资源的数据结构,及对该共享数据结构进行实际操作的一组过程组成的资源管理程序两者共同组成的资源管理模块。
经典进程的同步问题
此次仅记录一些经典的解法。
生成者-消费者问题
- AND 信号量解决方案。满足生产者和消费者均要用
mutex
考察对方是否进行操作,也要考察缓冲区情况empty
和full
。Var mutex,empty,full: semaphore:=1,n,0; buffer:array[0,…,n-1] of item; in out: integer:=0,0; begin parbegin producer: begin repeat produce an item in nextp; Swait(empty,mutex); buffer(in):=nextp; in:=(in+1)mod n; Ssignal(mutex,full); until false; end consumer:begin repeat Swait(full,mutex); Nextc:=buffer(out); Out:=(out+1) mod n; Ssignal(mutex,empty); consumer the item in nextc; until false; end parend end
哲学家进程问题
- AND 信号量解决方案。防止均拿起一只筷子的死锁。
Var chopsiick array of semaphore:=(1,1,1,1,1); process i repeat think; Sswait(chopstick[(i + 1)mod5], chopstick[i]); // 等待左右两根筷子均可使用,否则一个都不占用 eat; Ssignat(chopstick[(i + 1)mod5], chopstick[i]); until false;
读者-写者问题
- 利用信号量集机制解决方案。读者人数不超过 RN, 写者至多 1 个,读写不共存。
Var RN integer; L, mx:semaphore:=N,1; begin parbegin reader:begin repeat Swait(L, 1, 1); // 等待L至少有一个,使用一个 Swait(mx, 1, 0); // 等待mutex至少有一个, 使用0个 perform read operation; Ssignal(L, 1); // 释放1个L,并通知 until false; end writer:begin repeat Swait(mx, 1, 1; L, RN, 0); perform write operation; Ssignal(mu, 1); until false; end parend end
进程通信
低级通信:通信的信息量少,且需要用户实现数据结构设置,数据传送、进程的互斥与同步等。 进程通信采用的高级进程通信:用户可直接利用操作系统提供的一种高效传输大量数据的通信方式,操作系统屏蔽实现细节,通信过程对用户透明。
- 进程同步:在于实现多个进程按照合理的顺序执行。
- 进程通信:进程之间的信息交互。
- 进程通信是一种手段,而进程同步是一种目的。
进程通信的类型和实现方式
- 管道:通过管道创建,读取和写入都要通过 pipe。只支持半双工通信,只能在父子进程或者兄弟进程中使用。
- FIFO:命名管道,去除了管道只能在父子进程中使用的限制。
- 消息队列:独立于读写进程存在,避免同步阻塞问题。
- 信号量:计数器,用于多进程共享对数据对象的访问。
- 共享存储:共享的一个给定的存储区,需要信号量同步控制。
- 套接字:实现不同机器间进程的共享。
线程
线程引入
- 为了缓解进程创建,撤销和切换时的开销,引入线程的机制。线程作为调度和分派的基本单元,但不作为资源分配的单位。
- 与进程不同与相似:都能被调度,具有并发性,不拥有资源(但可访问所隶属的进程的资源),系统开销更小,同一进程的线程间通信和同步控制较容易。
- 属性:轻型实体,独立调度和分派基本单位,可并发执行,共享进程资源。
- 多线程 OS 中的进程:作为资源分配单位,包含多个线程(至少一个),进程不是一个可执行的实体。
线程间的同步和通信
- 互斥锁 mutex:线程读/写一个共享数据段时,应为该数据段的 mutex 设置关锁命令。为了减少线程阻塞,有的系统会有 Trylock 机制访问,成功则返回成功状态码,失败则返回失败状态码但不阻塞线程。
- 条件变量:解决 mutex 会引起互斥死锁的情况,当需求一个共享资源时,不会将其他的 mutex 锁起。将条件变量和互斥锁一起使用。
- 信号量机制:分为私有信号量(同一进程的不同线程使用,属于某个进程中,系统无法恢复它给下一个请求该信号量的线程),公用信号量(不同进程的线程之间使用,属于系统保护的储存区域,由 OS 管理,在线程结束但未释放该公用信号量时,OS 可以自动回收并通知下一个使用线程)
线程的实现方式
- 内核支持线程(KST, Kernel Supported Threds):线程的创建、撤销和切换均依靠内核(包括用户进程中的线程和系统进程中的线程),每个线程有线程控制块。
- 多处理系统中,内核能够调用同一个进程的多个线程并执行。
- 线程阻塞时,内核可控制调度该进程的其他线程或其他进程的线程占用处理器;
- 内核支持的线程具有很小的数据结构和堆栈,线程切换快,开销小。
- 内核本身采用了多线程技术,提高系统执行速度和效率。
- 用户级线程(User Level Threads):存在于用户空间,线程的创建、撤销和线程之间的同步与通信等功能都无须 OS 调用来实现。同一进程的线程调度也无需内核参与。线程控制块也设置在用户空间。因此内核完全不知道用户级线程的存在。因此用户及线程的系统的调度以进程为单位进行的。
- 优点:
- 切换线程不需要使用内核空间。
- 调度算法是进程专用的。线程的管理和调度可以由进程自己决定。
- 用户级线程的实现与操作系统平台无关。所以用户级线程可以在不支持线程机制的操作系统平台上实现。
- 缺点:
- 系统调用的阻塞问题。一个线程阻塞则该进程的全部线程都阻塞。
- 单纯用户级线程中,多线程应用不能利用多处理器进行多重处理的优点。
- 优点:
- 组合方式:把 ULT 和 KST 方式进行组合。
线程的实现
内核支持线程的实现
- 内核在创建进程 PCB 时,同时创建 PTDA(Per Task Data Area)用于保存线程的 TCB,创建新线程,在相应进程的 PTDA 创建新的 TCB。线程的调度和切换与进程的调度和切换时分相似。
用户级线程的实现
- 运行时系统:实质是用于创建、撤销、切换用户级线程和实现线程的同步和通信、调度的函数的集合。当线程需要系统调用或获得系统资源时,将需求传送给运行时系统,由运行时系统来进行相应的系统调用和通过系统调用获得资源。
- 内核控制线程:轻型进程 LWP,每个进程拥有多个 LWP,每个 LWP 有自己的数据结构(如 TCB 等)。当有用户级线程运行时,将其连接到 LWP 上,此时可获得内核支持的线程的所有属性(包括系统调用)。可以构建“线程池”,多个用户级线程复用一个 LWP(当用户级线程不需要与内核通信时,不需要 LWP)。此时阻塞 LWP 不会阻塞用户级线程,只是用户级线程不能访问内核。