相关概念
- 临界资源与临界区
- 临界资源:同一时刻只能由一个进程使用的资源。
- 如打印机、磁带机、绘图仪等物理设备;由不同进程共享的消息队列、变量、数据、文件等软件资源
- 临界区:程序中访问临界资源的那一部分代码
- 进入区、退出区、剩余区:为了保证临界资源的互斥使用,每个进程在进入临界区前,必须检查该临界资源是否空闲。因此临界区前面必须有一段代码用于对临界资源的检查,这段代码称为进入区。同样的,临界区之后必须有一个退出区,表明进程已经释放了相应的临界资源。代码的剩余部分称为剩余区
- 临界资源:同一时刻只能由一个进程使用的资源。
- 同步与互斥
- 互斥关系:又称进程的间接相互制约关系。几个进程并发执行,由于需要互斥访问系统中的临界资源,因而形成的相互制约的关系。互斥是竞争关系
- 同步关系:又称进程的直接相互制约关系。几个进程为了完成一项任务而规定的执行顺序问题,是进程的执行先后问题。同步是协作关系
- 由于互斥和同步这两种制约关系的存在,进程在执行过程中,是否能获得处理器以及以何种速度向前推进,并不由进程本身控制,这就是进程的异步性。这可能导致进程以不正确的顺序访问共享资源,也可能造成进程每次的运行结果不一致。这些错误往往与时间有关,因此称为“时间相关的错误”。为防止这类错误,必须协调进程的执行顺序,确保它们按顺序执行
- 同步机制设计准则
- 空闲让进
- 忙则等待
- 有限等待:对于请求访问临界资源的进程,必须保证它能在有限时间内进入对应的临界区
- 让权等待:若一个进程无法进入临界区,应立即释放处理器,避免出现“忙等”,以提高处理器利用率
- 实现临界区互斥的基本方法
- 软件同步机制
- 单标志法
- 双标志先检查法
- 双标志后检查法
- Perterson算法
- 硬件同步机制
- 关中断方法
- 硬件指令方法
- TSL(TestAndSetLock)指令
- Swap指令(XCHG指令)
- 这七种方法都无法解决“让权等待”准则,因此有了下面的信号量机制
- 软件同步机制
在概念理解的基础上应用信号量机制实现多进程的同步和互斥问题
- 信号量
- 原理:在几个进程之间使用简单的信号来实现同步。在这个过程中,一个进程可以被阻塞在某个特定位置,直到它收到一个特殊信号,方可继续运行。一个信号量对应一种资源,信号量的值等于该资源的剩余数量。P(S)表示申请申请一个资源S,V(S)表示释放一个资源S
- 类型
- 整型信号量
- 记录型信号量:在整型信号量的基础上,增加了一个被阻塞进程的队列,用于链接所有等待相应资源的进程,从而实现让权等待
- 互斥量:在记录型信号量的基础上,规定信号量的值只能为0或1(信号量的值不再表示系统中的资源数量,而是表示某个临界区在此时能否访问),且加锁(P操作)和解锁(V操作)必须在同一个进程进行。
- 应用
- 利用信号量实现进程互斥:当两个进程需要互斥访问某一临界资源时,每个进程在访问临界区前需对互斥量mutex执行P操作,退出临界区后对互斥量mutex执行V操作
semaphore mutex = 1; // 互斥量的初始值为1 Process_A{ while (true){ P(mutex); // 加锁 访问临界区; V(mutex); // 解锁 剩余区; } } Process_B的代码同上
- 利用信号量实现进程同步:假设两个进程要协同修改一个文件,
P
A
P_A
PA先修改,
P
B
P_B
PB后修改,则在两个进程中分别执行P、V操作
semaphore S = 0; Process_A{ 修改文件; V(S); // 表示修改完毕 } Process_B{ P(S); // 判断修改是否完成 修改文件; }
- 利用信号量实现前驱关系:为每一个直接前后驱关系设置一个信号量,并初始化为0
- 利用信号量实现进程互斥:当两个进程需要互斥访问某一临界资源时,每个进程在访问临界区前需对互斥量mutex执行P操作,退出临界区后对互斥量mutex执行V操作
- 经典同步问题
- 生产者-消费者问题
- 哲学家进餐问题
- 读者-写者问题
- 管程
- 出现原因:虽然信号量机制可以解决进程的同步与互斥问题,但每个希望访问临界资源的进程都必须分别定义自己的PV操作,这样会使众多同步操作分散在各个进程中。不仅让系统难以管理,而且可能会因为误用同步操作,导致死锁发生。因此引入了新的进程同步工具----管程
- 管程的作用:管程是一个软件模块,进程对共享资源的请求、释放和其他操作都必须通过同一个管程来实现。当多个进程请求访问同一资源时,会根据资源的情况接受或拒绝,确保每次只有一个进程进入临界区,有效实现进程互斥
- 集中管理分散于不同进程的临界区
- 防止进程违反同步操作
- 便于用高级语言编写程序、检查程序的正确性
- 管程的特性:
- 共享性:管程可以被系统中的不同进程以互斥方式访问,是一种共享资源
- 安全性:管程的过程只能访问属于本管程的局部变量
- 互斥性:进程对管程的访问是互斥的。在任何时候,一个管程中只能有一个活动进程【【【【【【【????管程是临界资源,那么是否需要用一个管程来管理管程的访问???套娃???】】】
- 封装性:管程中的过程只能使用管程中的数据结构,同时,管程中的数据结构都是私有的,只能在管程中使用,进程可以通过调用管程中的过程来使用临界资源
- 管程的语法描述:将表达共享资源的数据结构和对其进行操作的进程封装在一个对象中,并封装了同步操作,从而对进程隐藏同步的细节,简化了调用同步功能的接口。管程由以下四部分组成:
- 程序的名称
- 程序本地的共享数据结构
- 一组对数据结构进行操作的程序
- 一条初始化本地共享数据的语句
Monitor monitor_name{ // 管程名 share variable declarations; // 共享变量说明 cond declarations; // 条件变量说明 public: // 能被进程调用的过程 void P1{ // 对数据结构操作的过程 ... } void P2{ ... } ... { // 管程主体 initialization code; // 初始化代码 ... } }
- 条件变量
为什么需要进程同步
现代操作系统中多道程序异步并发,虽然可以有效提高资源利用率,大大增加系统吞吐量,但也使得系统更加复杂。每个进程的运行结果存在不可再现性,以及程序的执行存在不确定性。
因此为了确保多个进程有序执行,需要在多道程序系统中引入进程同步机制。具体来说,单处理器系统中的进程同步机制有:软/硬件同步机制、信号量机制、管程机制等,这些机制用于保证程序执行结果的可再现性