2.4 进程同步

 

2.4 进程同步

进程同步是操作系统设计中的一个核心概念,它确保在多道程序环境下,多个并发执行的进程可以有序地共享资源和协作,避免因资源争抢和访问冲突导致的数据不一致性问题。以下是进程同步的详细介绍,包括其基本概念、临界资源、临界区以及同步机制应遵循的规则。

2.4.1 进程同步的基本概念

进程同步的目标是协调多个相关进程的执行次序,以确保它们能够按照一定的规则共享系统资源,并有效合作,从而保障程序执行的可再现性。

两种形式的制约关系
  • 间接相互制约关系:多个并发执行的程序因共享系统资源(如CPU、I/O设备)而产生相互制约关系。为了有序运行,对于临界资源的访问必须互斥进行。

  • 直接相互制约关系:由于任务协作需要,建立的进程间直接的合作关系。例如,一个生产者进程生产数据供消费者进程消费的模型,这要求对共享缓冲区的访问进行同步。

异步性是多道程序环境下进程的一个特点,它可能导致资源访问的不正确次序,进而造成执行结果的不一致。因此,需要通过进程同步机制对进程的执行次序进行协调。

临界资源 (Critical Resource)

临界资源是指那些不能被多个进程同时访问的资源。为了避免对临界资源的并发访问导致的冲突,必须采取互斥方式实现资源共享。生产者-消费者问题是进程同步问题中的一个经典案例,它强调了生产者和消费者之间必须同步,以确保缓冲区的正确使用。

临界区 (Critical Section)

临界区是指访问临界资源的那段代码。要保证进程互斥地进入自己的临界区,每个进程在进入临界区之前应先检查资源是否可用。进程同步的实现需要在进程的结构中明确区分进入区、临界区、退出区和剩余区,以保证对临界资源的互斥访问。

同步机制应遵循的规则
  1. 空闲让进:如果没有进程在临界区内,请求访问临界资源的进程应立即被允许进入。
  2. 忙则等待:如果已有进程处于临界区内,其他试图进入的进程必须等待。
  3. 有限等待:保证请求访问临界资源的进程在有限时间内能进入临界区。
  4. 让权等待:当进程无法进入临界区时,应释放处理器,避免进入忙等状态。

进程同步机制的设计和实现是操作系统中实现并发控制和保证系统稳定性、效率的关键。通过使用硬件同步机制、信号量机制、管程机制等,可以有效地实现进程之间的同步,保证程序执行的可再现性和系统资源的有效利用。

 

2.4.2 硬件同步机制

硬件同步机制为进程互斥进入临界区提供了基础支持,克服了仅依靠软件方法所面临的难度和局限性。通过特殊的硬件指令,如Test-and-SetSwap指令,计算机能够实现进程间的互斥,保证对共享资源的安全访问。以下是硬件同步机制的主要方法:

1. 关中断

关中断是一种简单的互斥实现方法,通过在进入临界区之前禁止中断,直到临界区操作完成并重新开启中断。这种方法确保了临界区内的操作不会被中断和其他进程干扰,从而保障了互斥性。然而,这种方法存在明显的缺点:

  • 滥用关中断权力可能造成系统不稳定。
  • 关中断期间系统无法响应其他重要事件,影响系统的实时性和效率。
  • 在多处理器系统中,关中断不能阻止其他处理器上进程的执行。
2. 利用Test-and-Set指令实现互斥

Test-and-Set(TS)指令是一种原子操作,用于检测并修改内存位置的值。这种指令通常用于实现锁机制,以保证在同一时刻只有一个进程能进入临界区。使用Test-and-Set指令时,系统为每个临界资源分配一个锁变量,通过原子地测试并设置该锁变量来实现互斥。

3. 利用Swap指令实现进程互斥

Swap(或XCHG)指令用于交换两个内存位置的内容,同样可以用于实现进程互斥。每个临界资源有一个全局锁变量,每个进程使用一个局部变量来尝试获取锁。通过Swap操作,进程可以原子地检查锁的状态并尝试加锁,实现互斥访问。

忙等与让权等待

虽然硬件指令能有效实现互斥,但它们通常导致忙等(busy-waiting)问题,即当一个进程无法进入临界区时,它会在循环中不断检查锁的状态,消耗CPU资源而不做实际工作。这种忙等待状态违反了“让权等待”的同步机制原则,即一个不能进入临界区的进程应该释放处理器,允许其他进程执行,以提高系统的整体效率。

硬件同步机制为进程互斥和同步提供了有效的基础,但它们更适合于简单的互斥问题。对于更复杂的同步问题,如协调多个进程的精确执行顺序,通常需要更高级的同步机制,如信号量和管程。

 

 

2.4.3 信号量机制

信号量机制,由荷兰学者Dijkstra于1965年提出,是进程同步中一个非常重要的概念。它旨在控制对共享资源的访问,确保多个进程能够协调地运行。信号量主要有整型信号量、记录型信号量、AND型信号量和信号量集几种形式,下面分别进行介绍。

1. 整型信号量

整型信号量是最初的形式,由一个整数表示资源的数量。它只能通过两个原子操作来访问:wait(S)signal(S)(也称为P和V操作)。wait(S) 操作会测试信号量S,如果S大于0,则进程继续执行并将S减1;如果S等于0,则进程进入等待状态。signal(S) 操作将S加1,如果有进程因S为0而等待,则唤醒这些进程。

2. 记录型信号量

为了解决整型信号量中忙等(busy waiting)的问题,记录型信号量被引入。它不仅包括一个表示资源数量的整型变量value,还包括一个进程队列list,用于存放等待该资源的进程。当一个进程执行wait操作而资源不足时,它会被阻塞并加入到等待队列中,而不是忙等待。当执行signal操作释放资源时,如果有进程在等待队列中,则唤醒队列中的一个或多个进程。

3. AND型信号量

AND型信号量是为了解决一个进程同时需要多个资源才能执行的问题。如果一个进程需要的所有资源都可用,它则一次性获得所有资源并执行;如果任何一个资源不可用,进程则等待,直到所有资源都变为可用。这样可以避免死锁的发生,确保资源的有效利用。

4. 信号量集

信号量集是对AND型信号量的扩展,允许进程一次性申请或释放多个资源,每种资源的数量可以不同。这样,信号量集不仅可以用于处理复杂的同步问题,还可以提高资源分配的效率,减少死锁的可能性。

信号量机制是操作系统中实现进程同步和互斥的基本工具,它通过简单的waitsignal操作就能有效地控制对共享资源的访问,保证进程间的协调运行。通过使用信号量,开发者可以编写出安全、高效的并发程序。

 

2.4.4 信号量的应用

信号量机制不仅用于实现进程间的互斥访问临界资源,还可用于控制进程间的同步,即确保进程间按照特定的顺序执行。以下是信号量机制的两种典型应用:进程互斥和前趋关系的实现。

1. 利用信号量实现进程互斥

为保证多个进程互斥地访问临界资源,可以为该资源设置一个互斥信号量(mutex),初始值设为1。进程在进入临界区前执行wait(mutex)操作,如果mutex为1,则进程进入临界区并将mutex设为0,阻止其他进程进入临界区。进程离开临界区后执行signal(mutex)操作,将mutex重新设为1,允许其他进程进入临界区。这种方法确保了任一时刻只有一个进程能访问临界资源。

// 伪代码示例
semaphore mutex = 1;

// 进程P1
while(1) {
    wait(mutex);
    // 临界区
    signal(mutex);
    // 剩余区
}

// 进程P2
while(1) {
    wait(mutex);
    // 临界区
    signal(mutex);
    // 剩余区
}
2. 利用信号量实现前趋关系

信号量还可以用来描述程序或语句之间的前趋关系。假设有两个并发执行的进程P1和P2,P1中有语句S1,P2中有语句S2,要求S1执行后再执行S2。这可以通过一个共享的信号量S(初值为0)来实现,将signal(S)操作放在S1后面,而在S2前面插入wait(S)操作。

// 进程P1中
S1;
signal(S);

// 进程P2中
wait(S);
S2;

若P2先执行,将会因S为0而阻塞,直到P1执行完S1并通过signal(S)操作使S变为1,这时P2才能继续执行S2。这样就实现了S1和S2之间的同步。

复杂前趋关系的实现

对于更复杂的前趋关系,可以通过设置多个信号量来控制程序段的执行顺序。例如,为了保证S1→S2, S3→S4等关系,可以为每一对前趋关系设置一个信号量,并在适当的位置插入waitsignal操作,以确保程序按照预定的顺序执行。

// 伪代码框架
P1() {S1; signal(a); signal(b);}
P2() {wait(a); S2; signal(c); signal(d);}
P3() {wait(b); S3; signal(e);}
P4() {wait(c); S4; signal(f);}
P5() {wait(d); S5; signal(g);}
P6() {wait(e); wait(f); wait(g); S6;}

在主程序中初始化所有的信号量,并并行启动所有进程(使用cobegin-coend结构或类似机制)。

信号量的这些应用展示了其在进程同步和互斥中的强大能力,能够灵活地控制进程间的执行顺序和资源访问,是操作系统并发控制的基础工具。

 

2.4.5 管程机制

管程机制是一种高级的进程同步工具,提供了比信号量更为结构化和封装的同步机制。它通过封装共享资源及其操作过程,有效地管理并发进程对共享资源的访问,同时简化了同步和互斥操作的实现。管程将数据结构、对数据的操作过程、以及同步机制封装在一个模块中,这个模块就是管程。

管程的定义

管程是由共享数据结构的声明、对该数据结构操作的一组过程、以及初始化代码组成的模块。它确保了每次只有一个进程可以执行管程内的过程,从而实现进程间的互斥。管程提供了一个明确的接口,通过这个接口进程可以访问共享资源,而无需担心数据一致性和进程同步的问题。

管程的组成
  1. 共享变量说明:定义管程内部的数据结构,这些变量仅能被管程内的过程访问。
  2. 条件变量说明:用于进程同步,允许进程在特定条件不满足时在条件变量上等待,直到某个进程改变了条件并通知等待的进程。
  3. 过程:定义了一组对共享数据结构进行操作的过程,这些过程由外部进程调用,用于访问和修改共享资源。
  4. 初始化代码:设置共享数据的初始状态。
条件变量和同步操作

条件变量是管程中用于同步的特殊类型变量,它们通过waitsignal操作来实现进程间的同步。wait操作使调用进程在条件变量上等待,直到另一个进程在相同的条件变量上执行signal操作。当条件变量上的signal操作被执行时,它会唤醒在该条件变量上等待的一个进程(如果有的话)。

管程与进程的区别
  • 数据结构:进程定义了私有数据结构(如PCB),而管程定义了可以由多个进程共享的数据结构(如消息队列)。
  • 操作:进程通过顺序程序执行操作,管程则主要进行同步操作。
  • 目的:进程实现系统的并发性,管程解决共享资源的互斥使用问题。
  • 工作方式:管程被动地等待被调用,而进程主动执行。
  • 并发性:进程间可以并发执行,但管程不能与其调用者并发。
  • 动态性:进程具有动态性,由创建和撤销管理,而管程作为资源管理模块,供进程调用。
条件变量的处理方式

处理因条件变量而阻塞的进程时,可以采取不同的策略:进程在执行signal后立即离开管程,允许被唤醒的进程继续执行;或者使signal操作成为过程体的最后一个操作,确保进程在通知等待进程后立即退出管程。

管程提供了一种结构化和安全的方式来编写并发程序,使得共享资源的管理更为简单和直观。通过封装共享资源及其操作,管程帮助开发者避免了许多并发编程中常见的错误,如死锁和竞争条件。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

夏驰和徐策

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

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

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

打赏作者

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

抵扣说明:

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

余额充值