OS Chapter 2 Processes Concurrency

并发
互斥与同步

死锁与饥饿

monitors 管程

并发

  • 通信
  • 共享、竞争
  • 同步
  • 处理机时间分配

 

进程间:竞争或是协作关系

竞争:写时要互斥,读时没有

协作

  • 通信:消息传递,可能引起死锁,可能饥饿;

互斥

空闲让进、忙着等待、有限等待、让权等待

  • 同一时间只有一个进程进入临界区
  • 使用完后要求退出临界区
  • 不允许死锁与饥饿
  • 如果没有进程在使用临界区则当有进程使用的时候要求立即允许,不能推迟使用

 

互斥的解决方法

  • 软件方法
  • 硬件方法
  • 信号量
  • 管程
  • 消息传递

软件方法

目前只能控制2个进程的互斥

通过内存里的一个仲裁程序来判定是否可以被用,经典的有Peterson 算法,Dekker's 算法。(通过一个全局变量来判断)

需要较高的处理代价

有逻辑错误的风险

 

硬件方法

会出现busy waiting现象,消耗机器时间;

  • 屏蔽中断
    • 屏蔽系统中断,但其代价很高(出现紧急情况无法及时解决)
    • 多处理器系统中无法应对另外处理器中的进程对资源的访问(不适用于多处理器)
  • 专业机器指令
    • 做成机器指令,机器指令周期内无法中断
    • 没进入临界区的进程会出现忙等待,浪费机器资源
    • 优点:可以让任意多的进程在单处理器或共享主存的多处理器中完成互斥访问
    • 缺点:存存忙等,占用处理器的时间;可以有饥饿现象(可能出现进程很久都得不到该资源);可能出现死锁(低优先级占有临界区,高优先级占有CPU,两者相互等待)

 信号量

也是共享变量,但有2个原子操作;

  • wait signal操作,需要一个队列来存放等待的进程
  • 信号量是一个整型变量
    • 非负整数
    • wait signal操作是原语
  • Wait操作s-1
    • 申请资源且可能阻塞自己(s<0)
  • Signal操作s+1
    • 释放资源并唤醒阻塞进程(s<=0)

 

wait(s);
/* 临界区 */
signal(s);

 

两种信号量

  • 通用信号量
    • 记录型,一个域为整型,另一个域为队列,队列元素为等待该信号量的阻塞进程(FIFO)
  • 二进制信号量
    • 整型值只能为0 1
 1 wait(s)
 2 s.count = s.count - 1;
 3 if(s.count < 0){
 4   block();
 5   insert P into s.queue;
 6 }
 7 
 8 
 9 signal(s)
10 s.count = s.count +1;
11 if(s.count <= 0)
12 {
13   wakeup the first P;
14   remove the P from s.queue;
15 }

类型:互斥信号量、资源信号量

wait操作用于申请资源;signal用于释放资源,有责任唤醒进程;

 (P,V操作现在很少用了,现在是wait, signal操作,P即wait,V即signal)

 

操作系统以系统调用的方式提供wait signal操作;

经典问题

1. 生产者/消费者问题

生产者产生数据放到缓存中;消费者从缓存中取出数据;

要求:

  • 一次只允许一个生产者或消费者访问缓存;(互斥)
  • 控制生产者与消费者的同步,不能向满的缓存中写数据,不能从空的缓存中读数据;(同步)

循环使用缓存,取模操作。

 1 const sizeofbuffer =  ;
 2 semaphore:
 3     s = 1; //互斥信号量,初始化为1    初始化为多少就是可以允许多少个进程访问 
 4     n = 0; //  可取资源信号量,初始为0
 5     e = sizeofbuffer; // 可写资源信号量,初始为最大值
 6 
 7 /* producer */
 8 while(1)
 9 {
10     wait(e);  //等待有资源可写,e-1, 如果e<0阻塞,否则写数据
11     wait(s);
12     /**写数据*/
13     signal(s);
14     signal(n); /* 写了一个数据,数据单元n+1,唤醒一个消费进程*/
15 }
16 
17 /* consumer */
18 while(1)
19 {
20     wait(n); // 等待有资源可取,n<0阻塞,否则取数据 
21   wait(s);
22    /* 取数据 */
23   signal(s);
24    signal(e); /* 取了数据,空单元e+1,唤醒一个写数据进程 */
25 }

 

生产者:1. 是否有可写资源即空单元wait(e), 2. 是否可以访问缓存wait(s)

消费者: 1. 是否有可读资源signal(n), 2. 是否可以访问缓存signal(s)

生产者与消费者共享s,互斥访问;

  • 进程必须先申请资源信号量,再申请互斥信号量
  • 同一进程中wait与signal语句只能嵌套不能交叉
  • 对同一个信号量的wait、signal操作不能在同一进程中

 

2.读者写者问题

若干读进程,若干写进程

特点:

  • 可以有多个读者,读进程只能读数据
  • 只能有一个写者,写进程只能写数据

方案:读者优先、写者优先(同时有读写进程到达时,让哪种进程优先进去)

 

读者优先 (简单)

指一旦有读者正在读数据,允许多个读者同时进入读数据,只有当全部读者退出才允许写者进入写数据;

写者易于出现饥饿;

 1 /* writer reader */
 2 const readcount = 0;
 3 semaphore x = wsem = 1;
 4 
 5 /* writer */
 6 while(1)
 7 {
 8     wait(wsem);
 9     /* 写数据 */
10     signal(wsem);
11 }
12 
13 
14 /* reader */
15 while(1)
16 {
17     wait(x); // 用来互斥访问变量readcount
18     readcount = readcount + 1;  //对变量readcount只能互斥访问
19     if(x == 1) wait(wsem); // 第一个读者要把后面所有的写者阻塞,不是第一个读者则不需要wait(wsem),不然读者也阻塞了
20     signal(x);
21     /*读数据*/
22     wait(x);
23     readcount = readcount - 1;
24     if(x == 0) signal(wsem);   //最后一个读者要归还使用权给写者
25     signal(x);
26 }

 

写者优先 (复杂)

 具体看资料,用的信号量多更复杂;

 

Monitors 管程

面向对象方法

  • 用信号量实现互斥,容易出错(顺序、位置出错)
  • 管理是用并发的语言编写的程序,将临界资料封闭起来,形成很多的库函数,管程可以锁定任何对象
  • 用管理实现互斥比信号量更简单

特点:

  • 管程是一个软件模块,由过程、数据和初始化语句组成(即一个封闭的类对象)
  • 本地局部变量
  • 进程实用临界资料通过调用管程里的过程实现
  • 一次仅允许一个进程使用管程

 

消息传递

可以实现互斥,同时还可以交换消息;

send(destination, message);

receive(source, message);

 

同步:

对发送方或接收方,可以实现阻塞或非阻塞消息机制;

  • 阻塞发送、阻塞接收
    • 接收与发送都采用阻塞发送、阻塞接收
    • 两个进程都会等待
  • 非阻塞发送、阻塞接收
    • 立即发送,接收方没收到消息就等待
  • 非阻塞发送、非阻塞接收
    • 都不用等待

 

消息被发送到一个共享的多个队列,队列被称为邮箱,一个进程往邮箱里发消息,其它进程(可以有多个)从邮箱里取消息;

端口:如果接收方只对应一个进程,则叫端口;

 

posted on 2014-11-10 16:53  He2Li  阅读(158)  评论(0)    收藏  举报