第二章 进程的描述与控制【操作系统】

前言

关于操作系统,
CSDN有很多的优秀博客。
在这里,
本文摘取其他博客内容,
并附上相关链接,
如有侵权,
联系删除,
仅供学习交流使用

推荐

操作系统专栏

实验与练习

实验 Linux Shell实现模拟多进程并发执行【操作系统】

经典 生产者-消费者线程【操作系统】
练习 苹果-桔子线程【操作系统】

实验 线程编程-加1操作为什么会出错?【操作系统】
实验 进程通信【操作系统】

第二章 进程的描述与控制

2.1 前趋图和程序执行

进程前言

2.1.1 前趋图

在早期未配置OS的系统和单道批处理系统中,程序的执行方式是顺序执行,即在内存中仅装入一道用户程序,由它独占系统中的所有资源,只有在一个用户程序执行完成后,才允许装入另一个程序并执行。

在多道程序系统中,由于内存中可以同时装入多个程序,使它们共享系统资源,并发执行。

2.1.2 程序顺序执行

程序执行
1. 程序顺序执行
一个应用程序由若干个程序段组成,每一个程序段完成一个特定的功能,在执行时,它们都需要按照某种先后次序顺序执行,仅当前一程序段完成后,才运行后一程序段。即便是一个程序段,也可能存在执行顺序问题。

2. 程序顺序执行时特征
1) 顺序性:处理机严格地按照程序所规定的顺序执行
2) 封闭性:程序运行时独占全机资源,资源的状态(除初始状态)只有本程序才能改变它,程序一旦开始执行,其执行结果不受外界因素影响。
3) 可再现性:只要程序执行时的环境和初始条件相同,当程序重复执行都可获得相同的结果。

2.1.3 程序并发执行

1. 程序的并发执行
只有在不存在前趋关系的程序之间才有可能并发执行,否则无法并发执行。

2. 程序并发执行时特征
引入了程序间的并发执行功能后,虽然提高了系统的吞吐量和资源利用率,但由于它们共享系统资源,以及它们为完成同一项任务而相互合作,致使在这些并发执行的程序之间必将形成相互制约的关系。

1) 间断性 具有“执行——暂停——执行”这种间断性地活动规律。
2) 失去封闭性 当系统中存在着多个可以并发执行的程序时,系统中的各种资源将为它们所共享,而这些资源的状态也由这些程序来改变,致使其中任一程序在运行时,其环境都必然会受到其他程序的影响。
3) 不可再现性 程序在并发执行时,由于失去了封闭性,也将导致其又失去了可再现性。


2.2 进程的描述

2.2.1进程的定义和特征

1. 进程的定义
进程
并发执行 —— 间断性 + 失去封闭性 + 不可再现性 ——决定了通常的程序是不能参与并发执行的

为了能使程序并发执行,并且可以对并发执行的程序加以控制和描述 —— 进程

为了使参与并发执行的每个程序(含数据)都能独立地运行,在操作系统中必须为之配置一个专门的数据结构,称为进程控制块(PCB,Process Control Block)。

系统利用PCB来描述进程的基本情况和活动过程,进而控制和管理进程。

进程实体(进程映像) = 程序段 + 相关的数据段 + PCB

一般情况下,把进程实体就简称为进程;创建进程,实质上是创建进程实体中的PCB;撤销进程,实质上是撤销进程实体中的PCB。

进程的定义:
1) 进程是程序的一次执行
2) 进程是一个程序及其数据在处理机上顺序执行时所发生的活动
3) 进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位

进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。
一个比喻

我们写好的⼀⾏⾏代码,为了让其⼯作起来,我们还得把它送进城(进程)⾥,那既然进了城
⾥,那肯定不能胡作⾮为了。城⾥⼈有城⾥⼈的规矩,城中有个专⻔管辖你们的城管(操作系
统),⼈家让你休息就休息,让你⼯作就⼯作,毕竟摊位不多,每个⼈都要占这个摊位来⼯作,
城⾥要⼯作的⼈多着去了。所以城管为了公平起⻅,它使⽤⼀种策略(调度)⽅式,给每个⼈
⼀个固定的⼯作时间(时间⽚),时间到了就会通知你去休息⽽换另外⼀个⼈上场⼯作。
另外,在休息时候你也不能偷懒,要记住⼯作到哪了,不然下次到你⼯作了,你忘记⼯作到哪
了,那还怎么继续?有的⼈,可能还进⼊了县城(线程)⼯作,这⾥相对轻松⼀些,在休息的
时候,要记住的东⻄相对较少,⽽且还能共享城⾥的资源。

2. 进程特征
进程和程序是两个截然不同的概念,除了进程具有程序所没有的PCB结构外,还具有下面一些特征:
1) 动态性:
进程的实质是进程实体的执行过程
它由创建而产生,由调度而执行,由撤销而消亡
进程实体有一定的生命期,而程序则只是一组有序指令的集合,并存在于某种介质上,其本身不具有活动的含义,因而是静态的。

2) 并发性:
多个程序实体同存在于内存中,且能在一段时间内同时运行。

3) 独立性:
进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。
凡未建立PCB的程序都不能作为一个独立的单位参与运行。

4) 异步性:
进程是按照异步方式运行的,即按各自独立的、不可预知的速度向前推进。正是源于此因,才导致了传统意义上的程序若参与并发执行,会产生其结果的不可再现性。为使进程在并发运行时虽具有异步性,但仍能保证进程并发执行的结果是可再现的,在OS中引入了进程的概念,并且配置相应的进程同步机制。

2.2.2 进程的基本状态及转换

1. 进程三种基本状态
由于多个进程在并发执行时共享系统资源,致使它们在运行过程中呈现间断性地运行规律。

三种基本状态:
1)就绪(Ready)状态: 处于准备好运行的状态,即进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行。

2)执行(Running)状态: 已获得CPU,其程序正在执行的状态

3)阻塞(Block)状态: 正在执行的进程由于发生某事件(如I/O请求、申请缓冲区失败等)暂时无法继续执行时的状态。OS会把处理机分配给另一个就绪进程,而让受阻进程处于暂停状态。
2. 三种基本状态的转换
操作系统【两状态、五状态、七状态】
操作系统-进程状态转换图

3. 创建状态和终止状态
1)创建状态: 创建一个进程的步骤:
首先由进程申请一个空白PCB —— 向PCB中填写用于控制和管理进程的信息 —— 为该进程分配运行时所必需的资源 —— 把该进程转入就绪状态并插入就绪队列中

如果进程所需的资源尚不能得到满足,比如系统尚无足够的内存使进程无法装入其中,此时创建工作尚未完成,进程不能被调度运行,于是把此时进程所处的状态称为创建状态。

2)终止状态: 两个步骤:
等待操作系统进行善后处理 —— 将其PCB清零 —— 将PCB空间返还系统

当一个进程到达了自然结束点,或是出现了无法克服的错误,或是被操作系统所终结,或是被其他有终止权的进程所终结,它将进入终止状态。进入终止状态的进程以后不能再执行,但在操作系统中依然保留一个记录,其中保存状态码和一些计时统计数据,供其他进程收集。一旦其他进程完成了对其信息的提取之后,操作系统将删除该进程,即将其PCB清零,并将该空白PCB返还系统。

2.2.3 挂起操作和进程状态的转换

挂起操作Suspend 和 激活操作Active —— 对进程的重要操作

当该操作作用于某个进程时,该进程将被挂起,意味着此时该进程处于静止状态。如果该进程正在执行,它将暂停执行。若原本处于就绪状态,则该进程此时暂不接受调度。与之对应的是激活操作。
1. 引入
引入挂起操作的原因,是基于系统和用户的如下需要:
1)终端用户的需要。 程序运行期间发现有可疑问题 —— 停止修改
2)父进程需要。挂起子进程,以考查和修改子进程,或协调各子进程活动
3)负荷调节需要。 工作负荷较重 —— 挂起一些不重要的进程
4)操作系统需要。 挂起某进程 —— 以检查运行中的资源使用或进行记账

2. 状态转换:
1)活动就绪Readya — Suspend — 静止就绪Readys
可以接受调度 —— 不再被调度执行
2)活动阻塞Blockeda — Suspend — 静止阻塞Blockeds
处于静止阻塞状态的进程在其所期待的事件出现后,将从静止阻塞变为静止就绪。
3)静止就绪Readys — Active — 活动就绪
4)静止阻塞Blockeds — Active — 活动阻塞Blockeda

3. 具有挂起状态的进程状态转换
在这里插入图片描述

l 阻塞 → 阻塞/挂起:OS通常将阻塞进程换出,以腾出内存空间

l 阻塞/挂起→ 就绪/挂起:当阻塞/挂起进程等待的事件发生时,可以将其转换为就绪/挂起。

l 就绪/挂起→ 就绪:OS需要调入一个进程执行。

l 就绪 → 就绪/挂起:一般,OS挂起阻塞进程。但是有时也会挂起就绪进程,释放足够的内存空间。

l 新 → 就绪/挂起(新→ 就绪):新进程创建后,可以插入到就绪队列或就绪,挂起队列,若无足够的内存分配给新进程,则需要新→ 就绪/挂起。


2.2.4 进程管理中的数据结构

进程管理中的数据结构

一方面,为了便于对计算机的各类资源(包括硬件和信息)的使用和管理,OS将它们抽象为相应的各种数据结构,以及提供一组对资源进行操作的命令,用户利用这些数据结构和操作命令来执行相关的操作,无需关系具体实现细节

另一方面,操作系统作为计算机资源的管理者,尤其是为了协调诸多用户对系统中共享资源的使用,它还必须记录和查询各类资源的使用及各类进程运行情况的信息,OS对于这些信息的组织和维护也是通过建立和维护各种数据结构的方式来实现的。

1. 操作系统中用于管理控制的数据结构
每个资源和每个进程都设置了一个数据结构,用于表征其实体 —— 资源信息表或进程信息表 —— 包含了资源或进程的标识、描述、状态等信息以及一批指针

通过这些指针,可将同类资源或进程的信息表,或者同一进程所占用的资源信息表分类链接成不同的队列,便于操作系统查找。

OS管理的这些数据结构一般分为以下四类:
内存表、设备表、文件表、进程表(进程控制块PCB)

2.进程控制块PCB的作用
PCB作为进程实体的一部分,记录了操作系统所需的,用于描述进程的当前情况以及管理进程运行的全部信息,是操作系统中最重要的记录型数据结构。
PCB的作用是使一个在多道程序环境下不能独立运行的程序(含数据)成为一个能独立运行的基本单位,一个能与其他进程并发执行的进程。

具体作用进一步阐述:
1)作为独立运行基本单位的标志
系统是通过PCB感知进程的存在的,PCB已成为进程存在于系统的唯一标识

2)能实现间断性运行方式
进程因阻塞而暂停运行时 —— 必须保留运行时的CPU现场信息

系统将CPU现场信息保存在被中断进程的PCB中,供其再次被调用时恢复

3)提供进程管理所需要的信息

当调度程序调度到某进程运行时,只能根据该进程PCB中记录的程序和数据在内存或外村中的始址指针,找到相应的程序和数据

在进程运行过程中,当需要访问文件系统中的文件或I/O设备时,也需要借助于PCB中的信息

还可根据PCB中的资源清单了解到该进程所需的全部资源等。

4)提供进程调度所需要的信息

PCB中提供了进程处于何种状态的信息

在进行调度时往往还需要了解进程的其他信息 —— 优先级、等待时间、已执行时间

5)实现与其他进程的同步通信

进程同步机制是用于实现诸进程的协调运行的,在采用信号量机制时,它要求在每个进程中都设置有相应的用于同步的信号量。在PCB中还具有用于实现进程通信的区域或通信队列指针。

3. 进程控制块PCB中的信息
1)进程标识符
用于唯一地标识一个进程。一个进程通常有两种标识符:

外部标识符内部标识符
方便用户(进程)访问方便系统对进程的使用
由字母、数字组成唯一的数字标识符,通常是一个进程的序号
创建者提供操作系统设置

2)处理机状态
主要是由处理机的各种寄存器中的内容组成的。

寄存器类型作用
通用寄存器用户程序可访问,用于暂存信息
指令计数器存放要访问的下一条指令的地址
程序状态字PSW含有状态信息,如条件码、执行方式、中断屏蔽标志等
用户栈指针用户进程与之相关的系统栈,存放过程、系统调用参数及调用地址

处理机处于执行状态时,正在处理的许多信息都是放在寄存器中。当进程被切换时,处理机状态信息都必须保存在相应的PCB中,以便在该进程重新执行时能再从断点继续执行。

3)进程调度信息
OS进行调度时 —— 须了解进程状态和有关调度的信息 ——

进程状态
进程优先级
进程调度所需的其他信息 与采用的调度算法有关
事件 阻塞原因
4)进程控制信息

①程序和数据的地址
②进程同步和通信机制
实现进程同步和进程通信时必需的机制,如消息队列指针、信号量等(全部或部分存放在PCB)
③资源清单
除了运行期间所需的全部资源(除CPU)外,还有一张已经分配到该进程的资源的清单
④链接指针
给出本进程(PCB)所在队列中的下一个进程的PCB的首地址
4. 进程控制块的组织方式

(1)线性方式
组织在一张线性表中 —— 实现简单、开销小,每次查找需要扫描整个表 —— 适合进程数目不多的系统

(2)链接方式
把同状态进程的PCB分别通过PCB的链接字链接成一个队列
形成就绪队列、若干个阻塞队列、空白队列
就绪队列 —— 按优先级排列,高的在前面
阻塞队列 —— 按阻塞原因的不同,排成多个阻塞队列

(3)索引方式
系统根据所有进程状态的不同,建立几张索引表,例如就绪索引表、阻塞索引表等,把各索引表在内存的首地址记录在内存的一些专用单元中。在每个索引表的表目中,记录具有相应状态的某个PCB在PCB表的地址。

2.3 进程控制

操作系统内核和处理器执行状态

2.3.1 操作系统内核

通常将一些与硬件紧密相关的模块(如中断处理程序等)、各种常用设备的驱动程序以及运行频率较高的模块(如时钟管理、进程调度和许多模块公用的一些基本操作),都安排在仅靠硬件的软件层次中,将它们常驻内存,即通常被称为OS内核。
目的:
1)便于对这些软件进行保护,防止遭受其他应用程序的攻击
2)可提高OS的运行效率

处理器的执行状态
为了防止OS本身及关键数据(如PCB等)遭受到应用程序有意或无意的破坏,通常也将处理机的执行状态分成系统态和用户态两种:

系统态(管态、核心态)用户态(目态)
较高特权较低特权的执行状态
能执行一切指令,能访问所有寄存器和存储区仅能执行规定的指令,访问特定的寄存器和存储区

一般情况下,应用程序只能在用户态运行,不能去执行OS指令及访问OS指令。

OS内核基本都包括了两大方面的功能:
1. 支撑功能
支撑功能 – 提供给OS其他众多模块所需要的一些基本功能

1)中断处理 — 内核最基本的功能,OS许多重要的活动,如各种类型的系统调用、键盘命令的输入、进程调度、设备驱动无不依赖于中断。

2)时钟管理 — 内核一项基本功能
时间片轮转调度 — 时间片用完时,便由时钟管理产生一个中断信号,促使调度程序重新进行调度。
在实时系统中的截止时间控制、批处理系统中的最长运行时间控制等

3)原语操作
原语 — 由若干个指令组成,用于完成一定功能的一个过程,区别是“原子操作”,即这个过程是一个不可分割的基本单位,执行过程中不允许中断。
原子操作在操作态下执行,常驻内存。
如用于对链表进行操作的原语、用于实现进程同步的原语等。

2. 资源管理功能
1)进程管理

功能模块运行频率较高 为多种功能模块所需要
进程调度与分派、进程创建和撤销 实现进程同步的原语、常用的进程通信原语
放在内核 — 提高OS性能

2)存储器管理
存储器管理软件的运行频率也比较高 — 如用于实现将用户空间的逻辑地址变换成物理地址的地址转换机构、内存分配和回收的功能模块、实现内存保护和对换功能的模块等。

放在内核中 — 保证存储器管理具有较高的运行速度。

3)设备管理
放在内核 — 由于设备管理和硬件(设备)紧密相关
如各类设备的驱动程序、用于缓和CPU与I/O设备速度不匹配矛盾的缓冲管理、用于实现设备分配和设备独立性功能的模块等。


进程控制
进程控制是进程管理中最基本的功能。
一般由OS的内核中的原语来实现。
主要包括:
1)创建新进程
2)终止已完成的进程
3)将因发生异常情况而无法继续运行的进程置于阻塞状态
4)负责进程运行中的状态转换

2.3.2 进程的创建

1. 进程的层次结构
OS中,允许一个进程(父进程)创建另一个进程(子进程),由此形成了一个进程的层次结构(进程树)。

1)子进程可以继承父进程所拥有的资源,例如继承父进程打开的文件、父进程分配到的缓冲区等。
2)当子进程被撤销时,应将其从父进程那里获得的资源归还给父进程。
3)此外,在撤销父进程时,也必须同时撤销其所有的子进程。
4)为了标识进程的家族信息 — PCB中设置了家族关系表项 — 标明父进程和所有的子进程。进程不能拒绝子进程的继承权。

值得注意的是,Windows中不存在任何进程层次结构的概念,所有进程都具有相同的地位。
如果一个进程创建另外的进程时,创建进程获得了一个句柄,其作用是可用来控制被创建的进程。但是,这个句柄是可传递的,获得了句柄的进程就拥有控制其他进程的权利。
进程之间不再是层次关系,而是获得句柄与否、控制与被控制的简单关系。

2. 进程图
描述一个进程的家族关系 — 有向树 — 父进程指向子进程

3. 引起创建进程的事件
四类典型事件:

1)用户登录。在分时系统中,用户在中断键入登录命令后,若登录成功,系统将为该用户创建一个进程,并把它插入到就绪队列中。

2)作业调度。在多道批处理系统中,当作业调度程序按一定算法调度到某个(些)作业时,将它们装入内存,为它们创建进程,并把它们插入就绪队列。

3)提供服务。当运行中的用户程序提出某种要求后,系统将专门创建一个进程来提供用户所需要的服务。
要求进行文件打印 — 操作系统创建一个打印进程 — 不仅可使打印进程与该用户进程并发执行,还便于计算为完成打印任务所花费的时间。

4)应用请求。上述三种情况都是系统内核为用户创建一个新进程;而这类事件则是用户进程自己创建新进程,以便使新进程以同创建者进程并发运行的方式完成特定任务。
某用户程序需要不断地先从键盘终端读入数据,继而再对输入数据进行相应的处理,然后,再将处理结果以表格形式在屏幕上显示。为了使这几个操作并发运行,以加速任务完成,可分别创建键盘输入进程、表格输出进程。

4. 进程的创建
系统中出现创建新进程的请求 — OS便调用进程创建原语Creat按以下步骤创建新进程:

1)申请空白PCB,为新进程申请获得唯一的数字标识符(内部标识符),并从PCB集合中索取一个空白PCB

2)为新进程分配其运行所需的资源,包括各种物理和逻辑资源,如内存、文件、I/O设备和CPU时间等 ---- 从操作系统或父进程获得。

新进程对这些资源的需求详情一般也要提前告知操作系统或其父进程。
例如,为新进程的程序和数据以及用户栈分配必要的内存空间时,操作系统必须知道新进程所需内存的大小:

批处理作业 其大小可在用户提出创建进程要求时提供
为应用进程创建子进程 在该进程提出创建进程的请求中给出所需内存的大小
交互型作业 用户可不给出内存要求而由系统分配一定的空间;如果新进程要共享某个已在内存的地址空间(即已装入内存的共享段),则必须建立相应链接
3)初始化进程控制快(PCB)。
包括:
1.初始化标识信息,将系统分配的标识符和父进程标识符填入新的PCB中
2.初始化处理机状态信息,使程序计数器指向程序的入口地址,使栈指针指向栈顶。
3.初始化处理机控制信息,将进程状态设置为就绪状态或静止就绪状态,通常设置为最低优先级,除非用户以显式方式提出高优先级要求。

4)如果就绪队列能够接纳新进程,便将新进程插入就绪队列。

2.3.3 进程的终止

进程的终止
1. 引起终止的事件
引起终止的事件
1)正常结束 — 任何系统中都应有一个用于表示进程已经运行完成的指示

2)异常结束 — 发生了某种异常事件
1越界错 — 程序所访问的存储区,已越出该进程的区域
2保护错 — 程序试图去访问一个不允许访问的资源或文件 或 以不适当的方式进行访问
3非法指令 — 程序试图去执行一条不存在的指令。出现该错误的原因可能是程序错误地转移到数据区,把数据当成了指令
4特权指令错 — 用户程序试图去执行一条只允许OS执行的指令
5运行超时 — 进程执行时间超过了指定的最大值
6等待超时 — 进程等待某事件的时间超过了规定的最大值
7算术运算错 — 进程试图去执行一个被禁止的运算,如被0除
8I/O故障

3)外界干预 — 应外界请求而终止运行
1操作员或OS干预 — 系统中发生了某事件,例如系统死锁,由操作员和OS采取终止某些进程的方式使系统从死锁状态中解救出来
2 父进程请求 — 子进程完成父进程所要求的任务,父进程可提出请求结束子进程
3 因父进程终止 — 所有子进程应当结束

2. 进程的终止过程
终止过程 — 调用终止原语
1.根据被终止进程的标识符,从PCB集合中检索出该进程的PCB,从中读出该进程的状态
2.若被终止进程正处于执行状态,立即终止进程运行,并置调度标志为真,用于指示该进程被终止后应重新进行调度
3.若由子孙进程,应将其子孙进程都予以终止
4.将被终止进程拥有的全部资源归还给系统或父进程
5.将被终止进程(PCB)从所在队列(或链表)中移出,等待其他程序来搜集信息。

2.3.4 进程的阻塞和唤醒

1. 引起进程的阻塞和唤醒的事件

1)向系统请求共享资源失败。
系统无足够资源分配给它,进程不能继续运行而转变为阻塞状态。

2)等待某种操作的完成。
当进程启动某种操作后,如果该进程必须在该操作完成之后才能继续执行,则应先将该进程阻塞起来,以待操作完成。

3)新数据尚未到达。
对于相互合作的进程,如果一个进程需要先获得另一个进程提供的数据才能对其进行处理,只要需要的数据尚未到达,进程便只有阻塞。

4)等待新任务的到达。
某些系统中,特别是在网络环境下的OS,往往设置一些特定的系统进程,每当这种进程完成任务后便把自己阻塞起来,等待新任务的到来。

2. 进程阻塞过程

正在执行的进程,发生了上述事件
1)进程便通过调用阻塞原语block将自己阻塞。可见阻塞是进程自身的一种主动行为。

2)进入block过程后,由于该进程还处于执行状态,所以应先立即停止执行,把进程控制块中的现行状态由“执行”改为阻塞,并将PCB插入阻塞队列
(如果系统中设置了不同事件而阻塞的多个阻塞队列,则应将本进程插入到具有相同事件的阻塞队列)

3)转调度程序进行重新调度,将处理机分配给另一就绪进程,并进行切换(保留被阻塞进程的处理机状态在PCB中,再按新程序的PCB中的处理机状态设置CPU环境)。

3. 进程唤醒过程

当被阻塞进程所期待的事件发生时
由有关进程(比如提供数据的进程)调用唤醒原语wakeup,将等待该事件的进程唤醒

wakeup执行的过程是:
1)首先把被阻塞的进程从等待该事件的阻塞队列中移出,将其PCB中的现行状态由阻塞改为就绪,然后再将该PCB插入到就绪队列中

注意,block原语和wakeup原语是一对作用刚好相反的原语,必须成对使用,即如果在某进程中调用了阻塞原语,则必须在与之相合作的、或其他相关的进程中安排一条相应的唤醒原语,以便能唤醒该被阻塞进程;否则,阻塞进程将会因不能被唤醒而永久地处于阻塞状态,再无机会继续运行

2.3.5 进程的挂起和激活

1. 进程的挂起
当系统中出现了引起进程挂起的事件时,OS将利用挂起原语suspend将指定进程或处于阻塞状态的进程挂起。
suspend的执行过程是:
首先检查被挂起进程的状态,若处于活动就绪状态,便将其改为静止就绪;对于活动阻塞状态的进程,则将之改为静止阻塞;
为了方便用户或父进程考查该进程的运行情况,而把该进程的PCB复制到某指定的内存区域;
最后,若被挂起的进程正在执行,则转向调度程序重新调度。

2. 进程的激活过程
当系统中发生激活进程的事件时,OS将利用激活原语active,将指定进程激活。
active的执行过程是:
激活原语先将进程从外存调入内存,检查该进程的现行状态,若是静止就绪,便将之改为活动就绪;若为静止阻塞,便将之改为活动阻塞。
假如采用的是抢占调度策略,则每当有静止就绪进程被激活而插入就绪队列时,便应检查是否要进行重新调度,即由调度程序将被激活的进程与当前进程二者的优先级进行对比 ,如果被激活的进程的优先级低,就不必重新调度;若它高,则重新调度,将处理机分配给刚被激活的进程。

2.4 进程同步

进程同步
进程、线程基础知识

2.4.1 进程同步的基本概念

为保证多个进程能有条不紊低运行,在多道程序系统中,必须引入进程同步机制——硬件同步机制、信号量机制、管程机制

主要任务:对多个相关进程在执行次序上进行协调,使并发执行的诸进程之间能按照一定的规则(或时序)共享系统资源,并能很好地相互合作,从而使程序的执行具有可再现性。

1. 两种形式的制约
两种形式的制约 —— 对于同处于一个系统中的多个进程
1)间接相互制约关系——互斥
多个程序在并发执行时,由于共享系统资源,致使在这些并发执行的程序之间形成相互制约的关系。
对于像打印机、磁带机这样的临界资源,必须保证多个进程对之只能互斥地访问。
由此,在这些进程间形成了源于对该类资源共享的所谓间接相互制约关系。为保证这些进程能有序地运行,对于系统中的这些资源,必须由系统实施统一分配,即用户在要使用之前,应先提出申请,而不允许用户进程直接使用。

2)直接相互制约关系——同步
某些有应用程序,为了完成某任务而建立了两个或多个进程,这些进程将为完成同一项任务而相互合作。
进程间的直接相互制约关系就是源于它们之间的相互合作。

2.临界资源
许多硬件资源如打印机、磁带机等,都属于临界资源,诸进程间应采取互斥方式,实现对这种资源的访问。

3. 临界区
临界区 —— 每个进程中访问临界资源的那段代码
每个进程在进入临界区之前,应先对欲访问的临界资源进行检查,看其是否正被访问。如果此刻临界资源未被访问,进程便可进入临界区对该资源进行访问,并设置它正被访问的标志;如果此刻该临界资源正被访问,则本进程不能进入临界区。
所以,必须在临界区前面增加一段用于进行上述检查的代码 —— 进入区,相应地,在临界区后面增加一段用于将临界区正被访问的标志恢复为未被访问的标志 —— 退出区,除了上述之外的其他部分的代码 —— 剩余区

while(TRUE)
	{
		进入区(entry section);
		临界区(critical section);
		退出区(exit section);
		剩余区(remainder section);
	}

4. 同步机制四条准则
同步机制四条准则:

空闲让进当无程序处于临界区,表明临界资源处于空闲状态,应允许一个请求进入临界区的进程立即进入临界区,以有效地利用临界资源
忙则等待当已有进程进入临界区时,表明临界资源正在被访问,因而其他试图进入临界区的进程必须等待,以保证对临界资源的互斥访问
有限等待对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,以免陷入 “死等” 状态
让权等待当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入 “ 忙等” 状态

2.4.2 硬件同步机制

软件方法解决互斥进入临界区 —— 有一定难度,并且存在很大的局限性,现已很少采用

硬件方法 —— 利用计算机提供的一些特殊的指令来解决临界区问题,但当临界资源忙碌时,其他访问进程必须不断地进行测试,处于一种“忙等”状态,不符合“忙等让权”的原则,造成处理机时间的浪费,同时也很难将它们用于解决复杂的进程同步问题

在对临界区进行管理时,可将标志看做一个锁,初始时锁是打开的。
每个要进入临界区的进程必须先对锁进行测试,锁未开等待直至锁开,锁开立即将其锁上。
为防止多个进程同时测试到锁为打开的情况,测试和关锁操作是连续的,不允许分开进行。

1. 关中断
关中断 —— 最简单的方法之一
进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断 —— 计算机系统不响应中断,保证锁测试和关锁操作的连续性和完整性

缺点:
1)滥用关中断权力可能导致严重后果
2)关中断时间过长,会影响系统效率,限制了处理器交叉执行程序的能力
3)关中断方法也不适用于多CPU系统,因为在一个处理器上关中断并不能防止进程在其他处理器上执行相同的临界段代码

2. 利用 Test-and-Set指令实现互斥
利用 Test-and-Set指令实现互斥 —— 硬件指令 —— 一条原语

boolean TS(boolean *lock)
{
	boolean old;
	old = *lock;
	*lock = TRUE;       //TRUE表示资源正被使用,FLASE表示资源空闲
	return old;
}

用TS指令管理临界区时,为每个临界资源设置一个布尔变量lock,表示该资源的状态,初始值为FALSE,表示资源空闲。

在进入临界区之前,首先用TS指令测试lock,如果值为FALSE,则表示没有进程在临界区内,可以进入,并将lock置为TRUE,等效于关闭了临界资源,使任何进程都不能进入临界区。

do{
	while TS(&lock);
	critical section;   // 临界区
	lock = FALSE;
	remainder section;
}while(TRUE);

3. 利用Swap指令实现进程互斥
利用Swap指令实现进程互斥 —— 对换指令 —— 用于交换两个字的内容
用对换指令可以简单有效地实现互斥,方法是为每个临界资源设置一个全局的布尔变量lock,其初值为false,在每个进程中再利用一个全局布尔变量key。

do{
	key = TRUE;
	do{
		swap(&lock,&key);
	}while(key != FALSE);
	临界区操作;
	lock = FALSE;
	剩余区;
}while(TRUE);

2.4.3信号量机制

在长期且广泛应用中,信号量机制得到了很大的发展
整型信号量 —— 记录型信号量 —— “信号量集”机制
1. 整型信号量
整型信号量
整型量S —— 表示资源数目(除初始化外,仅能通过P、V操作)
wait(S)、signal(S) —— 两个标准的原子操作,分别称为P、V操作

wait(S){
	while(S <= 0);    /*do no-op*/  即什么都不做
	S--;
}

signal(S){
	S++;
}

P、V操作是两个原子操作,因此在执行时不可中断,亦即,当一个进程在修改某信号量时,没有其他进程可同时对该信号量进行修改。

整型信号量中的wait操作,只要是信号量S<=0,就会不断地测试。因此,该机制并未遵循“让权等待”的准则,而是使进程处于“忙等”的状态。
2. 记录型信号量
记录型信号量 —— 一种不存在“忙等”现象的进程同步机制
为了防止采取“让权等待”的策略后,会出现的多个进程等待访问同一临界资源的情况,除了需要一个用于代表资源数目的整型变量value外,还应增加一个进程链表指针list,用于链接上述的所有等待进程。记录型信号量是由于采用了记录型数据结构而得名的。

//数据结构
typedef struct{
	int value;
	struct process_control_block *list;
}semaphore;

wait(semaphore S){
	S->value--;
	if(S->value < 0) block(S->list);   
}

signal(semaphoree S){
	S->value++;
	if(S->value <= 0) wakeup(S->list);  //value <= 0 说明还有等待进程 

wait()中当S-value < 0时,进程调用block原语进行自我阻塞,放弃处理机,并插入到信号量链表S->list中,此时S->value的绝对值表示在该信号量链表中已阻塞进程的数目。

signal()中当释放一个资源后S->value <= 0,则表示在该信号量链表中仍有等待资源的进程被阻塞,应调用wakeup原语,将S->list链表中第一个等待进程唤醒。

如果S->value初值为1,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。
3. AND型信号量
AND信号量 —— 一个进程往往需要获得两个或更多的共享资源

AND同步机制的基本思想是:将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其他所有可能为之分配的资源也不分配给它。
也就是说,对若干个临界资源的分配采取原子操作方式:要么把它所请求的资源全部分配给进程,要么一个也不分配。

Swait(S1,S2,...,Sn)
{
	while(TRUE)
	{
		if(S1>=1 && ... && Sn>=1){
			for(i=1; i<=n; i++) Si--;
			break;
		}else{
		place the process in the waiting queue associated with the 
		first Si found with Si < 1, and set the program count of 
		this process to the beginning of Swait operation
		(将此进程放到第一个Si<1的等待队列,并将程序指针指向该进程的Swait
		操作开始处)
		}
	}
}

Ssignal()
{
	while(TRUE)
	{
		for(i=1; i<=n; i++)
		{
			 Si++;
			 Remove all the process waiting in the queue associated
			 with Si into the ready queue
			 (将所有与Si相关的等待队列中的进程移动到准备队列)
	}
}		

信号量集
前面所述的记录型信号量机制中,wait(S) 和 signal(S)操作仅能对信号量施以加1或减1操作,意味着每次只能对某类临界资源进行一个单位的申请或释放。

如果一次需要N个资源,便要进行N次wait操作,这显然是低效的而且会增加死锁的概率。此外,在有些情况下,为确保系统的安全性,当所申请某类资源数量低于某一下限值时,还必须进行管制,不予以分配。

因此,当进程申请某类临界资源时,在每次分配之前,都必须测试资源的数量,判断是否大于可分配的下限值,决定是否予以分配。

可以对AND信号量机制加以扩充,对进程所申请的所有资源以及每类资源不同的资源需求量,在一次P、V原语操作中完成申请或释放。

进程对信号量Si的测试值不再是1,而是该资源的分配下限ti,要求Si > ti,否则不予以分配。一旦允许分配,进程对该资源的需求值为di。

Swait(S1,t1,d1,…,Sn,tn,dn);
(将上述比较改成Si > ti ; 资源分配改成 Si = Si - di)
Ssignal(S1,d1,…,Sn,dn)
(将上述资源的释放改成Si = Si + di)

一般“信号量集”还有以下几种特殊情况:

1)Swait(S,d,d)。此时在信号量集中只有一个信号量S,但允许它每次申请d个资源,当现有资源数少于d时,不予以分配。

2)Swait(S,1,1)。
S>1 —— 一般的记录型信号量
S=1 —— 互斥信号量

3)Swait(S,1,0)。这是一种很特殊且很有用的信号量操作。当S>=1时,允许多个进程进入某特定区;当S变为0后,将阻止任何进程进入特定区。换言之,它相当于一个可控开关。

2.4.4信号量的应用

1. 利用信号量实现进程互斥
利用信号量实现进程互斥
为使多个进程能互斥地访问某临界资源,只需为该资源设置一个互斥信号量mutex,并设其初始值为1,然后将各进程访问该资源的临界区CS置于wait(mutex)和signal(mutex)操作之间即可。
利用信号量实现两个进程互斥的描述如下:

mutex,初值1,取值范围(-1,0,1)
1
mute取值 意义
1 两个进程都未进入需要互斥的临界区
0 一个进程进入临界区运行,另一个必须等待,挂入阻塞队列
-1 一个进程正在临界区运行,另外一个进程因等待而阻塞在信号量队列中,需要被当前已在临界区运行的进程退出时唤醒

semaphore mutex=1;
	P1(){                       
		while(1){
			wait(mutex);
			临界区;
			signal(mutex);
			剩余区;
		}
	}
	
	P2(){
		while(1){
			wait(mutex);
			临界区;
			signal(mutex);
			剩余区;
		}
	}

在利用信号量机制实现进程互斥时,wait(mutex)和signal(mutex)必须成对地出现。
缺少wait(mutex) —— 不能保证对临界资源的互斥访问
缺少signal(mutex) —— 临界资源永不释放,从而使因等待该资源而阻塞的进程不能被唤醒
2. 实现前驱关系
实现前驱关系
P1有语句S1,P2有语句S2,需要在S1执行之后再执行S2
为实现这种前驱关系,只需使进程P1和P2共享同一个信号量S,并赋予其初值为0,将signal(S)放在S1后面,wait(S)放在S2前面。

即进程P1 —> S1; siganl(S);
进程P2 —> wait(S); S2;

2.4.5 管程机制

虽然信号量机制是一种既方便、又有效的进程同步机制,但每个要访问临界资源的进程都必须具备同步操作wait(S)和signal(S)。这就使大量的同步操作分散在各个进程中。这样不仅给系统的管理带来了麻烦,而且还会因同步操作的使用不当而导致系统死锁。
1. 管程的定义

系统中的各种硬件资源和软件资源均可用数据结构抽象地描述其特性,即用少量信息和对该资源所执行的操作来表征该资源,而忽略它们的内部结构和实现细节。

因此,可以利用共享数据结构抽象地表示系统中的共享资源,并且将对该共享数据结构实施的特定操作定义为一组过程。进程对共享资源的申请、释放和其他操作必须通过这组过程,间接地对共享数据结构实现操作。对于请求访问共享资源的诸多并发进程,可根据资源的情况接受或阻塞,确保每次仅有一个进程进入管程,执行这组过程,使用共享资源,达到对共享资源所有访问的统一管理,有效地实现进程互斥。

管程 —— 代表共享资源的数据结构以及由对该共享数据结构实施操作的一组过程所组成的资源管理程序共同构成了一个操作系统的资源管理模块
管程被请求和释放资源的进程所调用。

Hansan对其定义:“一个管程定义了一个数据结构和能为并发进程所执行的一组操作,这组操作能同步进程和改变管程中的数据”

管程由四部分组成:
1)局部于管程的共享数据结构说明
2)对该数据结构进行操作的一组过程
3)对局部于管程的共享数据设置初始值的语句

管程包含了面向对象的思想,将表征共享资源的数据结构及其对数据结构操作的一组过程,包括同步机制,都封装在一个对象内部,隐藏了实现细节。
封装于管程内部的数据结构仅能被封装于管程内部的过程所访问,任何管程外的过程都不能访问它;
反之,封装于管程内部的过程也仅能访问管程内的数据结构。

所有过程要访问临界资源时,都只能通过管程间接访问,而管程每次只准许一个进程进入管程,执行管程内的过程,从而实现了进程互斥。

管程的特性解释
模块化管程是一个基本程序单位,可以单独编译
抽象数据类型管程中不仅有数据,而且有对数据的操作
信息屏蔽管程中的数据结构只能被管程中的过程访问,这些过程是在管程内部定义的,供管程外的进程调用,而管程中的数据结构以及过程(函数)的具体实现外部不可见
管程进程
公共数据结构(如消息队列)私有数据结构(PCB)
进行同步操作和初始化操作由顺序程序执行相关操作
为了解决共享资源互斥使用问题为了实现系统的并发性
被动工作方式(被进程调用)主动工作方式
管程不能与调用者并发进程之间可以并发执行
操作系统中的一个资源管理模块具有动态性,创建而生,撤销而亡

2. 条件变量
管程的条件变量
当某进程通过管程请求获得临界资源而未能满足时,管程便调用wait原语使该进程等待,并将其排在等待队列上。仅当另一进程访问完成并释放资源后,管程才又调用signal原语,唤醒等待队列中的队首过程。

如果一个进程调用了管程,在管程中时被阻塞或挂起,直到阻塞或挂起的原因解除,在此期间,如果该进程不释放管程,则其他进程无法进入管程,被迫长时间的等待。

为了解决这个问题,引入了条件变量condition,一个进程被阻塞或挂起的条件(原因)可有多个,因此在管程中设置了多个条件变量,对这些条件变量的访问只能在管程中进行。

管程中对每个条件变量都予以说明,其形式为condition x,y; 对条件变量的操作仅仅是wait和signal,因此条件变量也是一种抽象数据类型,每个条件变量保存了一个链表,用于记录因该条件变量而阻塞的所有进程,同时提供的两个操作即可表示为

1)x.wait: 正在调用管程的进程因x条件需要被阻塞或挂起,则调用x.wait将自己插入到x条件的等待队列上,并释放管程,直到x条件变化。此时其他进程可以使用该管程。

2)x.signal:正在调用管程的进程发现x条件变化,则调用x.signal,重新启动一个因x条件而阻塞或挂起的进程,如果存在多个,则选择其中的一个,如果没有,继续执行原进程。

如果有进程Q因x条件处于阻塞状态,当正在调用管程的进程P执行了x.signal操作后,进程Q被重启,此时有两种方式:
1)P等待,直至Q离开管程或等待另一条件
2)Q等待,直至P离开管程或等待另一条件


2.5 经典进程的同步问题

经典 生产者-消费者线程【操作系统】
练习 苹果-桔子线程【操作系统】

2.6 进程通信

进程通信

进程通信是指进程之间的信息交换。

2.6.1 进程通信的类型

高级通信机制可归纳为四类:
共享存储器系统
消息传递系统
管道通信系统
客户机—服务器系统
1. 共享存储器系统
共享存储器系统 Shared-Memory System
两种类型:

基于共享数据结构的通信方式基于共享存储区的通信方式
进程公用某些数据结构,借以实现进程间的信息交换,操作系统仅提供共享存储器,由程序员负责对公用数据结构的设置及对进程间同步的处理为了传输大量数据,在内存中划出了一块共享存储区域,诸进程可通过对该共享区的读或写交换信息,实现通信,数据的形式和位置甚至访问控制都是由进程负责,而不是OS
低级通信,仅适于传递相对少量的数据,通信效率低下高级通信,需要通信的进程在通信前,先向系统申请获得共享存储区的一个分区,并将其附加到自己的地址空间中,便可对其中的数据进行正常读、写,读写完成或不再需要时,将其归还给共享存储区

2. 管道(pipe)通信系统
管道(pipe)通信系统
管道 —— 指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,又名pipe文件。

向管道提供输入的发送进程(写进程)以字符流形式将大量的数据送入管道;而接收管道输出的接收进程(读进程)则从管道中接收数据。
这种方式首创于UNIX系统,由于它能有效地传送大量数据,因而又被引入到许多其他操作系统中。

为了协调双方的通信,管道机制必须提供以下三方面的协调能力:
1)互斥,当一个进程正在对pipe执行读/写操作时,其他(另一个)进程必须等待。
2)同步,当写(输入)进程把一定数据写入pipe,便去睡眠等待,直到读(输出)进程取走数据后再把它唤醒。当读进程读一空pipe时,也应睡眠等待,直至写进程将数据写入管道后才将之唤醒。
3)确定对方是否存在,只有确定了对方已存在时才能进行通信。
3. 消息传递系统 Message passing system —— 高级通信方式
在此机制中,进程不必借助任何共享存储区或数据结构,而是以格式化的消息为单位,将通信的数据封装在消息中,并利用操作系统提供的一组通信命令(原语),在进程间进行消息传递,完成数据间的数据交换。

该方式隐藏了通信实现细节,使通信过程对用户透明化,降低了通信程序设计的复杂性和错误率,成为当前应用最为广泛的一类进程通信间通信的机制。
例如:计算机网络中的报文;微内核操作系统中的微内核与服务器的通信;
这机制很好地支持多处理机系统、分布式系统和计算机网络,因此成为这些领域最主要的通信工具。

根据实现方式不同,分成两类:

直接通信方式间接通信方式
发送进程利用OS所提供的发送原语,直接把消息发送给目标进程发送和接收进程,都通过共享中间体(称为邮箱)的方式进行消息的发送和接收,完成进程间的通信

4. 客户机—服务器系统 client-Server system
客户机—服务器系统 client-Server system
客户机-服务器系统的通信机制,在网络环境的各种应用领域已成为当前主流的通信实现机制,其主要的实现方法分为三类:
套接字、远程过程调用和远程方法调用

套接字 —— 一个通信标识类型的数据结构,包含了通信目的的地址、通信使用的端口号、通信网络的传输层协议、进程所在的网络地址,以及针对客户或服务器程序提供的不同系统调用(或API函数)等,是进程通信和网络通信的基本构件。

基于文件型基于网络型
通信进程都运行在同一台机器的环境中,套接字是基于本地文件系统支持的,一个套接字关联到一个特殊的文件,通信双方通过对这个特殊文件的读写实现通信,类似管道通常采用的是非对称方式通信,即发送者需要提供接收者命名,通信双方的进程运行在不同主机的网络环境下,被分配了一对套接字,一个属于接收进程(服务端),一个属于发送进程(客户端)。发送进程发出连接请求时,随机申请一个套接字,主机为之分配一个端口,与该套接字绑定,不再分配给其他进程,接收进程拥有全局公认的套接字和指定的端口,并通过监听端口等待客户请求。因此任何进程都可以向它发出连接请求和信息请求,以方便进程之间通信连接的建立。接收进程一旦收到请求,就接受来自发送进程的连接,完成连接,当通信结束时,系统通过关闭接受进程的套接字撤销连接。

套接字优势:不仅适用于同一台计算机内部的进程通信,也适用于网络环境中不同计算机间的进程通信。由于每个套接字拥有唯一的套接字号,这样系统中所有的连接都持有唯一的一对套接字及端口连接,对于来自不同应用程序进程或网络连接的通信,能够方便地加以区分,确保了通信双方之间逻辑链路的唯一性,便于实现数据传输的并发服务,而隐藏了通信设施及实现细节,采用统一的接口进行处理。

远程过程调用和远程方法调用
远程过程调用RPC(Remote Process Call) —— 一个通信协议,用于通过网络连接的系统。该协议允许运行于一台主机系统上的进程调用另一台主机系统上的进程


2.6.2消息传递通信的实现方式

消息传递通信的实现方式
1. 直接消息传递系统
直接消息传递系统—— 利用OS所提供的发送命令(原语)

直接通信原语
1)对称寻址方式 —— 要求发送进程和接收进程都必须以显式方式提供对方的标识符。
系统提供以下两条通信命令:
send(receiver,message);
receive(sender,message);

不足:一旦改变进程的名称,则可能需要检查所有其他进程的定义,有关对该进程旧名称的所有引用都必须查找到,以便将其修改为新名称,显然,这样的方式不利于实现进程定义的模块化。

2)非对称寻址方式 —— 接收进程可能需要与多个发送进程通信,无法事先指定发送进程。因此,在接受进程的原语中,不需要命名发送进程,只填写表示源进程的参数,即完成通信后的返回值,而发送进程仍需要命名接收进程。
send(receiver,message);
receive(id,message); 接收来自任何进程的消息,id变量可设置为进行通信的发送方进程id或名字。

消息格式
比较短的定长消息格式 —— 减少对消息的处理和存储开销
变长的消息格式 —— 处理、存储方面付出更多的开销,方便用户

进程的同步方式
在完成消息的发送或接收后,存在三种情况:
1)发送进程阻塞,接收进程阻塞 —— 这种情况主要用于进程之间紧密同步,发送进程和接收进程之间无缓冲时。
2)发送进程不阻塞,接收进程阻塞 —— 发送进程不阻塞,因而它可以尽快地把一个或多个消息发送给多个目标;而接收进程平时处于阻塞状态,直到发送进程发来消息时才被唤醒
3)发送进程不阻塞,接收进程不阻塞 —— 发送进程和接收进程都在忙于自己的事情,仅当发生某事件使它无法继续运行时,才把自己阻塞起来等待

通信链路
通信双方建立链路,两种方式:

1)由发送进程在通信之前用显式的“建立连接”命令(原语)请求系统为之建立一条通信链路,在链路使用完成后拆除链路 —— 主要用于计算机网络

2)发送进程无须明确提出建立链路的请求,只须利用系统提供的发送命令(原语),系统会自动地为之建立一条链路 —— 主要用于单机系统中

根据通信方式的不同,把链路分成两种:
1)单向通信链路
2)双向通信链路
2. 信箱通信
间接通信方式 —— 信箱通信
进程之间的通信,需要通过某种中间实体来完成,该实体建立在随机存储器的公用缓冲区上,用来暂存发送进程发送给目标进程的消息;接收进程可以从该实体中取出发送进程发送给自己的消息,通常把这种中间实体称为邮箱,每个邮箱都有唯一的标识符。消息在邮箱中可以安全地保存,只允许核准的目标用户随时读取。既可实现实时通信,又可实现非实时通信。

信箱的结构 —— 一种数据结构,两个部分
1)信箱头,用以存放有关信箱的描述信息 —— 信箱标识符、信箱的拥有者、信箱口令、信箱的空格数等
2)信箱体 —— 由若干个可以存放消息的信箱格组成,信箱格的数目以及每格的大小是在创建信箱时确定的

信箱通信原语
1)邮箱的创建和撤销
进程可利用邮箱创建原语来建立一个新邮箱,创建者进程应给出邮箱名字、邮箱属性(公用、私用或共享);对于共享邮箱,还应给出共享者的名字,当进程不再需要读邮箱时,可用邮箱撤销原语将之撤销。
2)消息的发送和接收。当进程之间要利用邮箱进行通信时,必须使用共享邮箱,并利用系统提供的通信原语进行通信。
Send(mailbox,message);
Receive(mailbox,message);

信箱的类型
1)私用邮箱。用户进程可为自己建立一个新邮箱,并作为该进程的一部分。邮箱的拥有者有权从邮箱中读取消息,其他用户则只能将自己构成的消息发送到该邮箱中,可采用单向通信链路的邮箱来实现。当拥有该邮箱的进程结束时,邮箱也随之消失。

2)公用邮箱。由操作系统创建,并提供给系统种的所有核准进程使用。核准用户既可把消息发到该邮箱,也可从邮箱读取给自己的消息,应采用双向通信链路的邮箱来实现。在系统运行期间始终存在。

3)共享邮箱。由某进程创建,在创建时或创建后指明它是可共享的,同时须指出共享进程的名字。邮箱的拥有者和共享者都有权从邮箱中取走发送给自己的消息。

发送进程和接收进程存在以下四种关系:
1)一对一
2)多对一
3)一对多
4)多对多
————————————————

2.6.3 直接消息传递系统的实例

直接消息传递系统实例|操作系统
1.消息缓冲队列通信机制中的数据结构
1)消息缓冲区

typedef struct message_buffer
{
	int sender;   //发送者进程标识符
	int size;     //消息长度
	char *text;   //消息正文
	struct message_buffer *next;  //指向下一个消息缓冲区
}

2)PCB有关通信的数据项
在操作系统中采用消息缓冲队列通信机制时,除了需要为进程设置消息缓冲队列以外,还应该在进程的PCB中增加消息队列的首指针,用于对消息队列进行操作,以及用于实现同步的互斥信号量(mutex)和资源信号量(sm)

在PCB增加的数据项

typedef struct processcontrol_block
{
	...
	struct message_buffer *mq;   //消息队列队首指针
	semaphore mutex;             //消息队列互斥信号量
	semaphore sm;                //消息队列资源信号量
	...
}PCB

2.发送原语
发送进程在利用发送原语发送消息之前,应先在自己的内存空间设置一发送区a,把带发送的消息正文,发送进程标识符,消息长度等填入其中

//a是发送区首地址
void send(reciver,a)
{
	getbuf(a.size,i);        //根据a.size申请缓冲区
	i.sender = a.sender;
	i.size = a.size;
	copy(i.text,a.text);     //将发送区a中的消息复制到缓冲区i中
	i.next=0;
	getid(PCBset,reciver.j); //获得接受进程内部的标识符j
	wait(j.mutex);           //j.mq消息队列是临界资源,操作前要wait()
	insert(&j.mq,i);
	signal(j.mutex);
	signal(j.sm);
}

3.接受原语
接收进程调用接收原语receive(b),从自己的消息缓冲队列mq中摘下第一个消息缓冲队列i,并将其中的数据复制到以b为首地址的指定消息接受区内。

void receive(b)
{
	j = internal name;    //接收进程内部的标识符
	wait(j.sm);
	wait(j.mutex);
	remove(j.mq,i);       //将消息队列中的第一个消息移出
	signal(j.mutex);
	b.sender = i.sender;
	b.size = i.size;
	copy(b.text,i.text);   //将消息缓冲区i中的信息复制到接受区b中
	releasebuf(i);         //释放消息缓冲区
}

————————————————

2.7 线程(Threads)的基本概念

操作系统(十一)线程的概念和特点


计算机操作系统(第四版)之进程要点梳理

2.7.1 线程的引入

引入进程,是为了使多个程序能并发执行,以提高资源利用率和系统吞吐量。引入线程,则是为了减少程序在并发执行时所付出的时空开销,使 OS 具有更好的并发性。

1.进程的两个基本属性
1)进程是一个可拥有资源的独立单位。
2)进程同时又是一个可独立调度和分派的基本单位。
程序并发执行所需付出的时空开销。
由于进程是一个资源的拥有者,因而在创建、撤消和切换中,系统必须为之付出较大的时空开销,因此限制了系统中设置进程的数量。
线程
设法将进程的上述两个属性分开,由 OS 分开处理,亦即对于作为调度和分派的基本单位,不同时作为拥有资源的单位;而对于拥有资源的基本单位,又不对之进行频繁的切换。正是在这种思想的指导下,形成了线程的概念。

2.7.2 线程和进程的比较

线程又称为轻型进程或进程元,传统进程称为重型进程。

1.调度的基本单位
在传统的 OS 中,作为拥有资源的基本单位和独立调度、分派的基本单位都是进程。而在引入线程的操作系统中,则把线程作为调度和分派的基本单位,而进程作为资源拥有的基本单位。
在同一进程中,线程的切换不会引起进程的切换,但从一个进程中的线程切换到另一个进程中的线程时,将会引起进程的切换。
2.并发性
在引入线程的操作系统中,不仅进程之间可以并发执行,而且在一个进程中的多个线程之间亦可并发执行。
3.拥有资源
不论是传统的 OS,还是引入了线程的 OS,进程都可以拥有资源,并作为系统中拥有资源的一个基本单位。线程本身并不拥有系统资源,而是仅有一点必不可少的、能保证独立运行的资源,例如线程控制块等。
允许一个进程中的多个线程贡献该进程所拥有的资源,这主要表现在:属于同一进程的所有线程都具有相同的地址空间。
4.独立性
在同一进程中的不同线程之间的独立性要比不同进程之间的独立性低得多。因为进程之间是为了防止彼此干扰和破坏;而同一进程中的不同线程往往是为了提高并发性以及进行相互之间的合作而创建的。
5.系统开销
进程的创建、撤销和切换所付出的开销都明显大于线程的创建、撤销和切换的开销。
6.支持多处理机系统
对于单线程进程,不管多少处理机,同一时刻该进程只能运行在一个处理机上。对于多线程进程,就可以将一个进程中的多个线程分配到多个处理机上并行执行。

2.7.3 线程的状态和线程控制块

1.线程运行的三种状态
1)执行状态。2)就绪状态。3)阻塞状态。
2.线程控制块 TCB
所有用于控制和管理线程的信息都记录在线程控制块 TCB 中。线程控制块 TCB 中的信息:
1)线程标识符。2)一组寄存器。3)线程运行状态。4)优先级。5)线程专有存储器。6)信号屏蔽。7)堆栈指针,在指向的堆栈中通常保存有局部变量和返回地址。
3.多线程 OS 中的进程属性
1)进程是一个可拥有资源的基本单位。
2)通常一个进程中都含有若干个(至少一个)相对独立的线程。由进程为这些(个)线程提供资源及运行环境。多个线程可并发执行。
3)进程已不再是一个可执行的实体,而把线程作为独立运行的基本单位。虽然如此,进程仍具有与执行相关的状态。例如,所谓进程处于“执行”状态,实际上是指该进程中的某线程正在执行。此外,对进程所施加的与进程状态有关的操作,也对其线程起作用。例如,在把某个进程挂起时,该进程中的所有线程也都将被挂起;又如,在把某进程激活时,属于该进程的所有线程也都将被激活。

2.8 线程的实现

进程控制


计算机操作系统(第四版)之进程要点梳理

2.8.1 线程的实现方式

1.内核支持线程 KST
线程的创建、撤消和切换等都是在内核空间实现的。为每一个内核支持线程设置一个线程控制块以对内核线程进行控制和管理。
缺点:对于用户的线程切换而言,其模式切换的开销较大。因为用户进程的线程在用户态运行,而线程调度和管理是在内核实现的,系统开销较大。
2.用户级线程 ULT
用户级线程是在用户空间中实现的。对线程的创建、撤消、线程之间的同步与通信等功能,都无须内核的支持,即用户级线程是与内核无关的。
值得说明的是,对于设置了用户级线程的系统,其调度仍是以进程为单位进行的。
缺点:一个线程被阻塞,它所在的进程中的所有线程都会被阻塞。且多线程应用不能利用多处理机进行多重处理的优点,因为内核每次分配给一个进程的仅有一个 CPU,而进程中仅有一个线程能执行。
3.组合方式
把用户级线程和内核支持线程两种方式进行组合,提供了组合方式 ULT/KST 线程。一些内核支持线程对应多个用户级线程,这是用户级线程通过时分多路复用内核支持线程来实现的。
由于连接方式的不同,有三种不同的模型:
1)多对一模型。将用户级线程映射到一个内核控制线程,这些用户级线程一般属于一个进程。在任一时刻,只有一个线程能够访问内核,类似于前面的单纯的方式 2。
2)一对一模型。每一个用户级线程映射到一个内核支持线程。每创建一个用户线程,相应地就需要创建一个内核支持线程,开销较大。
3)多对多模型。许多用户级线程映射到同样数量或更少数量的内核支持线程上。克服了上述两种模型的缺点。

2.8.2 线程的实现

1.内核支持线程的实现
系统在创建一个新进程时,便为它分配一个任务数据区 PTDA,其中包括若干个线程控制块 TCB 空间。
每当进程要创建一个线程时,便为新线程分配一个 TCB,将有关信息填入该 TCB 中,并为之分配必要的资源。
有的系统中为了减少创建和撤消一个线程时的开销,在撤消一个线程时,并不立即回收该线程的资源和 TCB,当以后再要创建一个新线程时,便可直接利用已被撤消但仍保持有资源和 TCB 的线程作为新线程。
2.用户级线程的实现
用户级线程是在用户空间实现的。所有的用户级线程都运行在一个中间系统上。两种方式实现中间系统:
1)运行时系统。
所谓“运行时系统”,实质上是用于管理和控制线程的函数(过程)的集合,运行时系统中的所有函数都驻留在用户空间,并作为用户级线程与内核之间的接口。
不论在传统的 OS 中,还是在多线程 OS 中,系统资源都是由内核管理的。在传统的 OS 中,进程是利用 OS 提供的系统调用来请求系统资源的,系统调用通过软中断(如 trap)机制进入 OS 内核,由内核来完成相应资源的分配。用户级线程是不能利用系统调用的。当线程需要系统资源时,是将该要求传送给运行时系统,由后者通过相应的系统调用来获得系统资源的。
2)内核控制线程。
这种线程又称为轻型进程 LWP。LWP 可通过系统调用来获得内核提供的服务,这样,当一个用户级线程运行时,只要将它连接到一个 LWP 上,此时它便具有了内核支持线程的所有属性。这种线程实现方式就是组合方式。
每一个 LWP 都要连接到一个内核级线程上,这样,通过 LWP 可把用户级线程与内核线程连接起来,用户级线程可通过 LWP 来访问内核,但内核所看到的总是多个 LWP 而看不到用户级线程。
在内核级线程执行操作时,如果发生阻塞,则与之相连接的多个 LWP 也将随之阻塞,进而使连接到 LWP 上的用户级线程也被阻塞。

2.8.3 线程的创建和终止

在 OS 中有用于创建线程的函数(或系统调用)和用于终止线程的函数(或系统调用)。

1.线程的创建
应用程序在启动时,通常仅有一个“初始化线程”,它的主要功能是用于创建新线程。
2.线程的终止
有些线程(主要是系统线程),在它们一旦被建立起来之后,便一直运行下去而不再被终止。

————————————————

习题

计算机操作系统(第四版)课后习题答案(完整版)
计算机操作系统(第四版)课后习题答案西电版


最后

小❤️伙🧡 伴💚们💙点 🖤个 🤍赞 🤎呗

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日星月云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值