上机2(操作系统上机).pdf
《上机2(操作系统上机).pdf》由会员分享,可在线阅读,更多相关《上机2(操作系统上机).pdf(27页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第四章 同步和互斥 现代操作系统的中心方案是多道程序设计、多处理和分布式处理,这些方案的基础以及操作系统设计的基础是并发。当多个进程并发执行时,不论是在多处理器系统的情况下,还是单处理器多道程序系统中,都会产生解决冲突和合作的问题。并发进程可以按照多种方式进行交互。互相之间不知道对方的进程可能需要竞争的资源,如处理器时间或对 I/O 设备的访问。进程间由于共享访问一个公共对象,如主存中的一块空间或一个文件,可能间接知道对方,这类交互中产生的重要问题是互斥和死锁。互斥指的是,对一组并发进程,一次只有一个进程能够访问一个给定的资源或执行一个给定的功能。互知技术可以用于解决诸如资源争用之类的冲突,还
2、可以用于进程间的同步,使得它们可以合作。后一种情况的一个例子是生产者/消费者模型。为解决互斥问题,已经开发了许多软件算法,其中最著名的是 Dekker 算法。这种方法的处理开销很高,并且产生逻辑错误的危险也很高。支持互斥的第二种方法涉及到专门的及其指令,这种方法减少了开销,但由于使用了忙等待,因而仍然是低效的。支持互斥的另一种方法是在操作系统中提供功能,其中最常见的两种技术是信号量和消息机制。信号量用于在进程间发送信号,并可以很容易的用于实施一个互斥规定。消息对实施互斥是很有用的,它还为进程间的通信提供了一种有效的方法。在 Unix 和 Linux 中,可以有几种方式来实现同步互斥:?互斥锁和
3、条件变量?读写锁?记录上锁?Posix 信号量?System V 信号量 本章主要讨论解决互斥问题的两种方法,一种是软件算法,一种是信号量实现。4 4.1 互斥算法 互斥算法 软件方法是为在一个处理器或者共享主存的处理器机器上执行的并发进程实现的。4.1.2 Peterson 算法 Peterson 算法 Dekker 算法解决了互斥问题,但相对比较复杂,关于正确性证明也需要一定的技巧。Peterson 提出了一种简单而优雅的方案。和 Dekker 算法一样,全局数组变量 flag 表示每个进程关于互斥的位置,全局变量 turn 解决同时冲突。#include#include#define f
4、alse 0#define true 1 int flag2;int turn;void P0()while(true)flag0=true;turn=1;while(flag1&turn=1);/临界区 flag0=false;/其余代码 void P1()while(true)flag1=true;turn=0;while(flag0&turn=0);/临界区 flag1=false;/其余代码 int main()flag0=false;flag1=false;parbegin(P0,P1);显然,互斥可以得到保证。考虑进程 P0。一旦它把 flag0设置为 ture,P1 不能进入其临
5、界区;如果 P1 已经在自己的临界区中,则 flag1true,且 P0 被阻止进入临界区。另一方面,还可以防止互相阻塞。假设 P0 在它的 while 循环被阻塞,这意味着 flag1为 true 且turn1,则当 flag1 变成 false 或者当 turn 变成 0 时,P0 都可以进入自己的临界区。现在穷举三种情况:1、P1 不想进入临界区。这种情况是不可能的,因为它已经表示了想进入(flag1=false)。2、P1 正在等待进入它的临界区。这种情况也不可能,因此如果 turn1,P1 就能够进入它的临界区。3、P1 正在重复使用它的临界区,从而垄断了对临界区的访问。这不可能发生
6、,因此P1 在每次试图进入自己的临界区之前都被迫把 turn 设置为 0,从而给了 P0 进入的机会。因此,我们对两个进程间的互斥问题给出了一种简单的解决方案。此外,Peterson 算法可以很容易的推广到 n 个进程的情况。4.2 信号量 4.2 信号量 4.2.1 信号量原语 4.2.1 信号量原语 信号量可以看作一个具有整数值的变量,在它之上定义三个操作:1.一个信号量可以初始化成非负数。2.wait 操作使信号量减 1。如果值变成负数,则执行 wait 的进程被阻塞。3.signal 操作使信号量加 1。如果值不是正数,则被 wait 操作阻塞的进程被解除阻塞。wait 和 signa
7、l 原语被假设是原子的,也就是说,它们不能被中断,每个例程被看作是一个不可分割的步骤。一个二元信号量的值只能是 0 或 1。下面分别给出信号量和二元信号量原语的定义。下面给出使用信号量进行互斥的一个例子。const int n=/*进程数*/;struct semaphore enum(zero,one)value;queueType queue;void wait(semaphore s)if(s.value=1)s.value=0;place this process in s.queue;block this process void signal(semaphore s)if(s.qu
8、eue.is_empty()s.value=1;else remove a process P from s.queue;place process P on ready list;二元信号量原语的定义二元信号量原语的定义 struct semaphore int count;queueType queue;void wait(semaphore s)s.count-;if(s.count0)place this process in s.queue;block this process void signal(semaphore s)s.count+;if(s.count=0)remove
9、a process P from s.queue;place process P on ready list;信号量原语的定义信号量原语的定义 semaphore s=1;void P(int i)while(true)wait(s);/临界区 signal(s);/其余部分 void main()parbegin(P(1),P(2),.,P(n):4.3 Linux4.3 Linux/Unix 提供的同步机制 提供的同步机制 4.3.1 互斥锁和条件变量 互斥锁和条件变量 互斥锁和条件变量出自 Posix 1 线程标准,它们可用来同步一个进程内的各个线程。如果一个互斥锁或条件变量存放在多个进
10、程间共享的某个内存区中,那么 Posix 还允许它用于这些进程间的同步。互斥锁用于保护临界区,以保证任何时刻只有一个线程在执行其中的代码,或者任何时刻只有一个进程在执行其中的代码。互斥锁用于上锁,条件变量则用于等待。定义一个互斥锁:static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;以下三个函数给一个互斥锁上锁和解锁:#include int pthread_mutex_lock(pthread_mutex_t*mptr);int pthread_mutex_trylock(pthread_mutex_t*mptr);int pthrea
11、d_mutex_unlock(pthread_mutex_t*mptr);如果尝试给一个已经由另外某个线程锁住的互斥锁上锁,那么 pthread_mutex_lock 将阻塞到该互斥锁解锁为止。pthread_mutex_trylock 是对应的非阻塞函数,如果该互斥锁已经锁住,它就返回一个 EBUSY 错误。条件变量是类型为 pthread_cond_t 的变量,以下两个函数用于这些变量。#include int pthread_cond_wait(pthread_cond_t*cptr,pthread_mutex_t*mpthr);int pthread_cond_signal(pthre
12、ad_cond_t*cptr);每个条件变量总是有一个互斥锁与之关联。当调用 pthread_cond_wait 等待某个条件为真时,同时指定其条件变量的地址和所关联的互斥锁的地址。互斥锁和条件变量可以静态分配并静态初始化,它们也可以动态分配,那要求动态的初始化它们。动态初始化允许指定进程间共享属性,从而允许在不同进程间共享某个互斥锁或者条件变量,其前提是该互斥锁或者条件变量必须存放在由这些进程共享的内存区中。4.3.2 Posix 信号量 4.3.2 Posix 信号量 信号量(semaphore)是一种用于提供不同进程间或者一个给定进程的不同线程间同步手段的原语。Posix 信号量就是其中
13、一种。Posix 信号量分为两种:?Posix 有名信号,使用 Posix IPC 名字标识,可用于进程或者线程间的同步。?Posix 基于内存的信号,存放在共享内存中,可用于进程或者线程间的同步。Posix 有名信号量总是能在不同进程之间共享,基于内存的信号量则必须在创建时指定是否在进程间共享。这两类信号量的持续性也有差别,有名信号量至少有随内核的持续性。基于内存的信号量则具有随进程的持续性。Posix 信号量是计数信号量,它提供以下三种基本操作:?创建一个信号量?等待一个信号量的值变为大于 0,然后将它的值减 1?给一个信号量的值加 1,并唤醒等待该信号量的任意线程,挂起该信号量 与互斥锁
14、相比较,Posix 信号量还有一个互斥锁没有提供的特性,即互斥锁必须总是由锁住它的线程解锁,信号量的挂出却不必由执行过它的线程执行。有关 Posix 信号量的具体库函数以及其用法,我们将以生产者消费者模型为例,在实验中详细给出。4 4.3.3 System V 信号量 System V 信号量 System V 信号量增加了技术信号量集这一概念,即一个或者多个信号量构成的集合,其中每个都是计数信号量。从 Posix 信号量到 System V 信号量发生了如下变动:1)System V 信号量由一组值构成。当指定应用到某个信号量集的一组信号量操作时,要么所有操作都执行,要么一个操作都不执行。2
15、)可应用到一个信号量集的每个成员的操作有三种:测试其值是否为 0,往其中加一个整数以及从其值中减调一个整数。Posix 信号量所允许的操作只是将其加 1 或者减 1。3)创建一个 System V 信号量集需要技巧,因为创建该集合并随后初始化其各个值需要两个操作,从而可能导致竞争状态。4)System V 信号量提供“复旧”特性,该特性保证进程终止时反转某个信号量操作。4.4 生产者/消费者模型 4.4 生产者/消费者模型 生产者/消费者问题是并发处理中最常见的一类问题。通常可以描述如下:有一个或者多个生产者产生某种类型的数据(记录、字符),并放置在缓冲区中;有一个消费者从缓冲区中取数据,每次
16、取一项;系统保证避免对缓冲区的重复操作,也就是说,在任何时候只有一个代理(生产者或者消费者)可以访问缓冲区。下面给出使用信号量、二元信号量来实现有限或无限缓冲区的生产者/消费者问题的模型。int n;binary_semaphore s=1;binary_semaphore delay=0;void producer()while(true)produce()waitB(s);append();n+;if(n=1)signalB(delay);signalB(s);void consumer()int m;waitB(delay);while(true)waitB(s);take();n-;m
17、=n;signalB(s);consume();if(m=0)waitB(delay);void main()n=0;parbegin(producer,consumer);图图 使用二元信号量解决无限缓冲区生产者使用二元信号量解决无限缓冲区生产者/消费者问题消费者问题 semaphore n=0;semaphore s=1;void producer()while(true)produce()wait(s);append();signal(s);signal(n);void consumer()while(true)wait(n);wait(s);take();signal(s);consu
18、me();void main()parbegin(producer,consumer);图图 使用信号量解决无限缓冲区生产者使用信号量解决无限缓冲区生产者/消费者问题消费者问题 const int sizeofbuffer=/*缓冲区大小*/semaphore n0;semaphore s=1;semaphore e=sizeofbuffer;void producer()while(true)produce()wait(e);wait(s);append();signal(s);signal(n);void consumer()while(true)wait(n);wait(s);take(
19、);signal(s);signal(e);consume();void main()parbegin(producer,consumer);图图 使用信号量解决有限缓冲区生产者使用信号量解决有限缓冲区生产者/消费者问题消费者问题 4.5 读者/写者模型 4.5 读者/写者模型 读写问题定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间,甚至可以是一组处理其寄存器;有一些只读取这个数据区的进程(reader)和一些只往数据区中写数据的进程(writer);此外还必须满足以下条件:1.可以同时有多个读进程读取关键区;2.在某个时刻,只能有一个写进程修改关键区;3.如
20、果一个写进程正在修改关键区时,禁止任何读进程不能读关键区;对于读写问题,还分为读优先、写优先两种模型。读优先读优先 读优先的要求是只要至少有一个读进程在读取关键区,写进程就不能获取关键区的控制权。它的算法模型如下:int readcount;semaphore x=1,wsem=1;void reader()while(true)wait(x);readcount+;if(readcount=1)wait(wsem);signal(x);READUNIT();wait(x);readcount-;if(readcount=0)signal(wsem);signal(x);void waiter
21、()while(true)wait(wsem);WRITEUNIT();signal(wsem);void main()readcount=0;parbegin(reader,writer);对于以上算法中各个信号量的解释如下表:信号量 用途 Wsem 只要有一个写进程在修改关键区,其他写进程,读进程就不能访问关键区 第一个读进程需要 wait(mutex),看能否进入关键区 最后一个读进程需要 signal(mutex),允许写进程进入关键区 X 全局变量 readcount 记录读进程数目,x 用来保护 radcount 互斥 写优先 写优先的要求是当一个写进程准备访问关键区时,不允许新的
22、读进程访问该关键区(即当处于这样一个状态,系统没有任何读进程,因此可以调用新的读进程,也可以调用新的写进程,但是这时要调度写进程而不能是读进程。写优先的算法模型如下所示:int readcount,writecount;semaphore x=1,y=1,z=1,wsem=1,rsem=1;void reader()while(true)wait(z);wait(rsem);wait(x);readcount+;if(readcount=1)wait(wsem);signal(x);signal(rsem);signal(z);READUNIT();wait(x);readcount-;if(
23、readcount=0)signal(wsem);signal(x);void waiter()while(true)wait(y);writecount+;if(writecount=1)wait(rsem);signal(y);wait(wsem);WRITEUNIT();signal(wsem);wait(y);writecount-;if(writecount=0)signal(rsem);signal(y);void main()readcount=0;writecount=0;parbegin(reader,writer);以上写优先算法模型中各个信号量的含义是:信号量 用途 Ws
24、em 只要有一个写进程在修改关键区,其他写进程,读进程就不能访问关键区 第一个读进程需要 wait(mutex),看能否进入关键区 最后一个读进程需要 signal(mutex),允许写进程进入关键区 X 全局变量 readcount 记录读进程数目,x 用来保护 radcount 互斥 Rsem 当至少有一个写进程准备访问数据区时,用于禁止所有的读进程 writecount 控制 rsem 的设置 Y 控制 writecount 的更新 Z z 在 rsem 上只允许一个读进程排队,其他读进程在 z 上排队,从而使得写进程可以跳过 rsem 以上写优先算法模型中进程队列的状态如下表:系统中只
25、有读进程?设置 mutex(wsem)?没有队列 系统中只有写进程?设置 wsem,rsem?写进程在 wsem 上排队 既有读进程又有写进程,读优先既有读进程又有写进程,读优先?由读进程设置 wsem?由写进程设置 rsem?所有写进程在 wsem 上排队?一个读进程在 rsem 上排队?其他读进程在 z 上排队 既有读进程又有写进程,写优先?由写进程设置 wsem?由写进程设置 rsem?写进程在 wsem 上排队?一个读进程在 rsem 上排队?其他读进程在 z 上排队 4.6 实验三 多线程实现 Peterson 算法4.6 实验三 多线程实现 Peterson 算法 实验题目:实验题
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 上机 操作系统
限制150内