操作系统入门

文章目录

  • 页面置换算法
    • 1.最佳置换算法(OPT,OPTimal replacement)
    • 2.先进先出置换算法(FIFO,First-In First-out replacement)
    • 3.最近最少使用置换算法(LRU,Least Recently Used replacement)
    • 4.第二次机会页面替换算法(SCR,Second Chance Replacement)
    • 5.时钟置换算法(CLOCK,Clock policy replacement)
  • 银行家算法
      • 练习题
      • 题目一
      • 题目二
  • 磁盘调度算法
  • IO软件四个层次
    • 1.I/O中断处理程序
    • 2.I/O设备驱动程序
    • 3.独立于设备的I/O软件
    • 4.用户空间的I/O软件
  • 基础知识
    • 利用信号量实现同步
    • 利用信号量实现互斥
    • 利用信号量实现前驱关系
  • 解题思路
    • 一、关系分析
    • 二、整理思路
    • 三、设置信号量
  • 例题一
    • 生产者——消费者问题
      • 简单版
      • 复杂版
    • 读者——写者问题
    • 哲学家进餐问题
    • 吸烟者问题
  • 例题二
    • 读者——写者问题
    • 吸烟者问题
    • 橘子——苹果问题
    • 理发师问题
    • 生产与装配车间问题
    • 缓冲区问题
    • 哲学家进餐问题
    • 农夫猎人问题
    • 银行业务问题
    • 黑白棋子问题
    • 独木桥问题
    • 机房问题
    • 阅览室问题

页面置换算法

在多道程序正常运行过程中,属于不同进程的页面被分散存放在内存页框中,当发生缺页异常时,如果已无空闲页框,系统要选择一个驻留页面进行淘汰。在此讨论的是所有驻留页面都可作为置换对象的情况,而不管页面所属进程的全局页面置换算法。

1.最佳置换算法(OPT,OPTimal replacement)

当要调入一页而必须淘汰旧页时,应该淘汰以后不再访问的页,或距现在最长时间后才访问的页
然而,程序页面引用串是无法预知的,不可能对程序的运行过程做出精确断言,不过此理论算法可用做衡量各种具体算法的标准。

2.先进先出置换算法(FIFO,First-In First-out replacement)

实现方法:

  1. 系统中设置一张具有m个元素的页号表P[0],P[1],…,P[m-1],
    其中,每个P[i](i=0,1,.,m-1)存储一个装入内存中的页面的页号。
    假设用索引k指示当前调入新页时应淘汰页在页号表中的位置,
    则淘汰页的页号应是P[k]
    每当调入新页后,执行P[k]=新页的页号,k=(h+1)%m就可以了,
    因为它是在内存中驻留时间最长的页面。
  2. 页面缓冲算法
    系统维护两个FIFO队列——修改页面队列和非修改(空闲)页面队列,
    前者是由修改页面的页框所构成的链表;后者是由可直接用于装入页面的页框所构成的链表,只不过有些未修改的淘汰页暂时还留在其中,当进程再次访问这些页面时,可不经I/O操作而快速找回。
    当发生缺页中断时,按照FIFO算法选出淘汰页,并不立即抛弃它,而是根据其内容是否被修改过而进入两个队列之一的末尾,需要装入的页面被读进非修改队列的队首所指向的页框中,不必等待淘汰页写回,使得进程能快速恢复运行。当选中的淘汰页被写回磁盘时,只需把此页占用的页框链接到非修改队列的末尾即可。每当修改页面队列中的页面达到一定数量时,将成批地写回磁盘,并把空闲页框加入非修改页面队列尾部。

Belady异常,分配给进程的页框数目边多,缺页次数反而也跟着增多。

3.最近最少使用置换算法(LRU,Least Recently Used replacement)

为了能准确地淘汰最近最少使用的页面,必须维护一个特殊队列页面淘汰队列,此队列存放当前在内存中的所有页号,每访问一页时就调整一次,使队列尾总是指向最近访问的页,队列头就是最近最少使用的页,显然,发生缺页异常时总是淘汰队列头所指页面;而执行页面访问后,需要从队列中把此页调整到队列尾。
LRU算法的实现需要硬件支持,关键是确定页面最后访问以来所经历的时间,可采用多种模拟方法。

  1. 引用位法。
    又称最近未使用页面替换算法( Not Recently Used replacement ,NRU)。此方法为每页设置引用位R,每次访问某页时,由硬件将此页的R位置1,间隔时间t,周期性地将所有页的R位清0。页面置换时,从引用位R为0的那些页中挑选页面进行淘汰,在选中要淘汰的页面后,也将其他页面的引用位R清0。
    这种实现方法开销小,但t的大小不易确定且精确性差。t大了,缺页异常时所有页的R值均为1;1小了,缺页异常时可能所有页的R值均为0,这样就很难挑选出应淘汰的页面,通常把定为一个或数个时钟中断周期。
  2. 计数法。
    每当页面被引用时,页引用计数器自动计数,更换访问页面时,把页引用计数器的值记录到页表的计数值字段,经过时间t后,将所有页引用计数器全部清除。页面置换时,系统检查所有页表项,页引用计数值最小的页面就是最不经常使用的页,故称最不经常使用页面替换算法( Not Frequently Used replacement , NFU)
  3. 记时法。
    为每页增设一个记时单元,每当页面被引用时,把当前绝对时间置入记时单元。经过时间t后,将所有记时单元全部清除。页面置换时,系统对各页面的记时值进行比较,值最小的页面就是最久未使用的页面,从而淘汰之。
  4. 老化算法。
    为每个页设置一个多位寄存器r。 当页面被访问时,对应寄存器的最左边位置1;每隔时间t,将r寄存器右移一位;在发生缺页中断时,找最小数值的r寄存器对应的页面淘汰。

4.第二次机会页面替换算法(SCR,Second Chance Replacement)

FIFO算法会把经常使用的页面淘汰掉,为了避免这一点,可对算法进行改造,把FIFO算法与页表中的“引用位”结合起来使用,实现思想如下:首先检查FIFO页面队列中的队首,这是最早进入内存的页面,
如果其“引用位”是0,那么,这个页面最早进入并且最长时间未被使用,选择此页面淘汰;
如果其“引用位”是1 ,说明虽然它进人内存时间较早,但最近仍在使用,于是将其“引用位”清0,并把这个页面移至队尾,把它看做个新调入的页,再给一次机会。
这一算法称为第二次机会页面替换算法( Second Chance Replacement ,SCR),其含义是最先进入内存的页面如果最近还在被使用(其“引用位”总保持为1),仍然有机会像新调入页面一样留在内存中。如果内存中的页面都被访问过,即它们的“引用位”均为1 ,那么,第一遍检查把所有页面的“引用位”清0,第
二遍又找出队首,并把此页面淘汰,此时,SCR算法便退化为FIFO算法。

5.时钟置换算法(CLOCK,Clock policy replacement)

如果利用标准队列机制构造FIFO队列,SCR 算法可能产生频繁的出队和人队,实现代价较高。作为SCR的一种改进,可采用循环队列机制构造页面队列,形成类似于钟表面的环形表,队列指针相当于钟表表针,指向可能要淘汰的页面,这就是时钟页面替换算法(Clock policy replacement, Clock)的得名。此算法与SCR算法在本质上没有区别,仅仅是实现方法有所改进,仍要使用页表中的“引用位”,把进程已调人内存的页面链接成循环队列,用指针指向循环队列中下一个将被替换的页面。

银行家算法

练习题

题目一

设系统中有3种类型的资源(A、B、C)和4个进程(P1、P2、P3、P4),A资源的总量为9,B资源的总量为3,C资源的总量为6,在T0时刻系统状态如下表所示,系统采用银行家算法实施死锁避免策略。
操作系统入门_第1张图片
试问:T0时刻的各资源剩余数量为多少?T0时刻是否为安全状态?若是,请给出安全序列。

做题视频

答:

  1. 剩余
A B C
1 1 2
  1. 需求
need A B C
P1 2 2 2
P2 1 0 2
P3 1 0 3
P4 4 2 1
  1. 安全序列

P2,P3,P1,P4

题目二

设系统中有4中类型的资源(A、B、C、D)和5个进程(P0、P1、P2、P3、P4),A资源的总量为3,B资源的总量为12,C资源的总量为14,D资源的总量为14。在T0时刻系统中个资源使用情况的状态如下表所示,系统采用银行家算法实施死锁避免策略。
操作系统入门_第2张图片
试问:T0时刻的各资源剩余数量为多少?T0时刻的是否为安全状态?若是,请给出其中可能的一种安全序列,并依照该序列,写出各资源的回收步骤。(6分)

磁盘调度算法

“先来先服务”算法(FCFS)

磁盘臂随机移动。

“电梯调度”算法

每次总是选择沿移动方向最近的那个柱面,若同一柱面上有多个请求,还需进行旋转优化。

“最短查找时间优先”算法

先执行查找时间最短的请求。

“扫描”算法

移动臂每次沿一个方向移动,扫过所有柱面,遇到最近的I/O请求便进行处理,直到到达最后一个柱面后,再向相反的方向移动回来。

“循环扫描”算法

上述扫描算法中,到达最后一个柱面后,不是反方向,而是同一方向从另一端开始扫描。

IO软件四个层次

1.I/O中断处理程序

2.I/O设备驱动程序

3.独立于设备的I/O软件

4.用户空间的I/O软件

中断,中断就是遇到紧急事件时,系统暂时停止对当前程序的执行,转去执行紧急程序,待执行完毕后,再返回执行之前的程序或调度其他应用程序的过程。
中断如异常,时钟中断等
异常(内中断),指当前运行指令引起的中断时间,如地址异常,算术异常等
操作系统,计算机最基础的系统软件,管理软硬件资源,控制程序执行,改善人机界面,合理组织计算机流程,为用户使用计算机提供良好的运行环境,操作系统是计算机系统资源的管理者,用户与计算机之间的接口,扩充机器,操作系统的特征是并发,共享,虚拟,异步。
操作系统的基础抽象是进程,虚存,文件。其中进程是处理器的抽象,虚存是内存的抽象,文件是设备的抽象。

基础知识

利用信号量实现同步

信号量机制能用于解决进程间的各种同步问题。

例:进程P2中的语句y要使用进程P1中语句x的运行结果

  • 解:只有当语句x执行完成之后语句y才可以执行。

  • 所以要设置一个信号用来两个进程传递信息(x执行完成的信息)

  • 设S为实现进程P1、P2 同步的公共信号量,初值为0。

/**
 * P1说:P2你收到我发的信号再执行,所以P2一直等P1发信号
 */
semaphore S=0;
p1() {
    ...
    x;
    V(S);			//通知p2我执行完了语句x
    ...
}
p2() {
    ...
    P(S);
    y;
    ...
}

若P2先执行到P(S)时, S为0,执行P操作会把进程P2阻塞,并放入阻塞队列;
当进程P1中的x执行完后,执行V操作,把P2从阻塞队列中放回就绪队列,当P2得到处理机时,就得以继续执行。

利用信号量实现互斥

信号量机制也能很方便地解决进程互斥问题。

例:每次只允许一个进程进入临界区

  • 解:每个进程进入都要告诉别人我用了,让别人不要用
  • 每个进程进入前都要看别人有没有发送已经用的信号
  • 每个进程用完后都要告诉别人我用完了,允许别人使用
  • P一个信号量就可以实现实现两个操作:告诉别人不用(使1→0)等待别人不用(等0→1)
  • V一个信号量可以实现一个操作:告诉别人可以用(使0→1)
  • 设S为实现进程P1、P2互斥的信号量,
  • S的初值应为1(即可用资源数为1,刚开始肯定没人用,第一个进程可以直接进)。
  • 只需把临界区置于P(S)和V(S)之间,
  • 即可实现两个进程对临界资源的互斥访问。
semaphore S=1;
P1() {
    ...
    P(S);
    进程P1的临界区;
    V(S);
    ...
}
P2() {
    ...
    P(S);
    进程P2的临界区;
    V(S);
    ...
}

当没有进程在临界区时,任意一个进程要进入临界区,就要执行P操作,把S的值减为0,然后进入临界区;
当有进程存在于临界区时,S的值为0,再有进程要进入临界区,执行P操作时将会被阻塞,直至在临界区中的进程退出,这样便实现了临界区的互斥。
互斥是不同进程对同一信号量进行P、V操作实现的,一个进程成功对信号量执行了P操作后进入临界区,并在退出临界区后,由该进程本身对该信号量执行V操作,表示当前没有进程进入临界区,可以让其他进程进入。
下面简单总结一下PV操作在同步互斥中的应用:
在同步问题中,若某个行为要用到某种资源,则在这个行为前面P这种资源一下;
若某个行为会提供某种资源,则在这个行为后面V这种资源一下。
在互斥问题中,P、V操作要紧夹使用互斥资源的那个行为,中间不能有其他冗余代码。

利用信号量实现前驱关系

semaphore a1=a2=b1=b2=c=d=e=0;
S1() {
    ...
    V(a1);
    V(a2);
}
S2() {
    P(a1);
    ...
    V(b1);
    V(b2);
}
S3() {
    P(a2);
    ...
    V(c);
}
S4() {
    P(b1);
    ...
    V(d);
}
S5() {
    P(b2);
    ... 
    V(e);
}
S6() {
    P(c);
    P(d);
    P(e);
    ...
}

解题思路

一、关系分析

找出问题中的进程数,并分析它们之间的同步和互斥关系。
同步、互斥、前驱关系直接按照上面例子中的经典范式改写。

二、整理思路

找出解决问题的关键点,并根据做过的题目找出求解的思路。
根据进程的操作流程确定P操作、V操作的大致顺序。

三、设置信号量

根据上面的两步,设置需要的信号量,确定初值,完善整理。

例题一

生产者——消费者问题

简单版

  • 问题描述

一组生产者进程和一组消费者进程共享一个初始为空、大小为n的缓冲区,
只有缓冲区没满时,生产者才能把消息放入缓冲区,否则必须等待;
只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。
由于缓冲区是临界资源,它只允许一个生产者放入消息,或一个消费者从中取出消息。.

  • 问题分析

关系分析

生产者和消费者对缓冲区互斥访问是互斥关系,
同时生产者和消费者又是一个相互协作的关系,
只有生产者生产之后,消费者才能消费,它们也是同步关系。

整理思路

这里比较简单,只有生产者和消费者两个进程,
正好是这两个进程存在着互斥关系和同步关系。
那么需要解决的是互斥和同步PV操作的位置。

信号量设置。

信号量mutex作为互斥信号量,用于控制互斥访问缓冲池,互斥信号量初值为1;
信号量full用于记录当前缓冲池中的“满”缓冲区数,初值为0。
信号量empty用于记录当前缓冲池中的“空”缓冲区数,初值为n。

semaphore mutex=1;
semaphore empty=n;
semaphore full=0;
producer() {
    while(1) {
        produce an item in nextp;
        P(empty);
        P(mutex);
        add nextp to buffer;
        V(mutex);
        V(full);
    }
}
consumer() {
    while(1) {
        P(full);
        P(mutex);
        remove an item from buffer;
        V(mutex);
        V(empty);
        consume the item;
    }
}

该类问题要注意对缓冲区大小为n的处理,当缓冲区中有空时,便可对empty变量执行P操作,一旦取走一个产品便要执行V操作以释放空闲区。
对empty和full变量的P操作必须放在对mutex的P操作之前。
若生产者进程先执行P(mutex),然后执行P(empty),消费者执行P(mutex),然后执行P(full), 这样可不可以?
答案是否定的。设想生产者进程已将缓冲区放满,消费者进程并没有取产品,即empty=0,当下次仍然是生产者进程运行时,它先执行P(mutex)封锁信号量,再执行P(empty)时将被阻塞,希望消费者取出产品后将其唤醒。轮到消费者进程运行时,它先执行P(mutex),然而由于生产者进程已经封锁mutex信号量,消费者进程也会被阻塞,这样一来生产者、消费者进程都将阻塞,都指望对方唤醒自己,因此陷入了无休止的等待。同理,若消费者进程已将缓冲区取空,即full=0,下次若还是消费者先运行,也会出现类似的死锁。
不过生产者释放信号量时,mutex、 full 先释放哪一个无所谓,消费者先释放mutex或empty都可以。

复杂版

  • 问题描述

桌子上有一个盘子,每次只能向其中放入一个水果。
爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,
儿子专等吃盘子中的橘子,女儿专等吃盘子中的苹果。
只有盘子为空时,爸爸或妈妈才可向盘子中放一个水果;
仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出。

  • 问题分析

关系分析

这里的关系要稍复杂一些。由每次只能向其中放入一只水果可知,爸爸和妈妈是互斥关系。
爸爸和女儿、妈妈和儿子是同步关系,而且这两对进程必须连起来,
儿子和女儿之间没有互斥和同步关系,因为他们是选择条件执行,不可能并发。

整理思路

这里有4个进程,实际上可抽象为两个生产者和两个消费者被连接到大小为1的缓冲区上。

信号量设置

首先将信号量plate设置互斥信号量,表示是否允许向盘子放入水果,初值为1表示允许放入,且只允许放入一个。
信号量apple表示盘子中是否有苹果,初值为0表示盘子为空,不许取,apple=1表示可以取。
信号量orange表示盘子中是否有橘子,初值为0表示盘子为空,不许取,orange = 1表示可以取。

semaphore plate = 1, apple = 0, orange = 0;
dad() {
    while(1) {
        prepare an apple;
        P(plate);
        put the apple on the plate;
        V(apple);
    }
}
mom() {
     while(1) {
        prepare an orange;
        P(plate);
        put the orange on the plate;
        V(orange);
    }
}
son() {
     while(1) {
        P(orange);
        take an orange from the plate;
        V(plate);
        eat the orange;
    }
}
daughter() {
     while(1) {
        P(apple);
        take an apple from the plate;
        V(plate);
        eat the apple;
    }
}

读者——写者问题

  • 问题描述

有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。
因此要求:
①允许多个读者可以同时对文件执行读操作;
②只允许一个写者往文件中写信息;
③任一写者在完成写操作之前不允许其他读者或写者工作;
④写者执行写操作前,应让已有的读者和写者全部退出。

  • 问题分析

关系分析

由题目分析读者和写者是互斥的,写者和写者也是互斥的,而读者和读者不存在互斥问题。

整理思路

两个进程,即读者和写者。 写者是比较简单的,它和任何进程互斥,用互斥信号量的P操作、V操作即可解决。
读者的问题比较复杂,它必须在实现与写者互斥的同时,实现与其他读者的同步,因此简单的一对P操作、V操作是无法解决问题的。
这里用到了一个计数器,用它来判断当前是否有读者读文件。当有读者时,写者是无法写文件的,此时读者会一直占用文件,当没有读者时,写者才可以写文件。同时,这里不同读者对计数器的访问也应该是互斥的。

信号量设置。

首先设置信号量count为计数器,用于记录当前读者的数量,初值为0;
设置mutex为互斥信号量,用于保护更新count变量时的互斥;
设置互斥信号量rw,用于保证读者和写者的互斥访问。

  1. 读进程优先
int count = 0;//区分读-读和读-写,只有当count=0时读写才有关系
semaphore mutex = 1;//互斥访问count?
semaphore rw = 1;//实现写-读互斥、写-写互斥
writer() {
    while(1) {
        P(rw);
        writing;
        V(rw);
    }
}
reader() {
    while(1) {
        P(mutex);
        if(count == 0)
            P(rw);
        count++;
        V(mutex);
        
        reading;
        
        P(mutex);
        count--;
        if(count == 0)
            V(rw);
        V(mutex);
    }
}
  1. 读写公平法
int count = 0;
semaphore mutex = 1;
semaphore rw = 1;
semaphore w = 1;
writer() {
    while(1) {
        P(w);//等待没有写进程访问缓冲区
        P(rw);
        writing;
        V(rw);
        V(w);
    }
}
reader() {
    while(1) {
        P(w);
        P(mutex);
        if(count == 0)
            P(rw);
        count++;
        V(mutex);
        V(w);
        
        reading;
        
        P(mutex);
        count--;
        if(count == 0)
            V(rw);
        V(mutex);
    }
}

哲学家进餐问题

  • 问题描述

一张圆桌边上坐着5名哲学家,每两名哲学家之间,的桌上摆一根筷子,两根筷子中间是一碗米饭。
哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。
只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。
若筷子已在他人手上,则需要等待。
饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,进餐完毕后,放下筷子继续思考。

  • 问题分析

关系分析

5名哲学家与左右邻居对其中间筷子的访问是互斥关系。

整理思路

显然,这里有5个进程。 本题的关键是如何让一名哲学家拿到左右两根筷子而不造成死锁或饥饿现象。
解决方法有两个:
一是让他们同时拿两根筷子;
二是对每名哲学家的动作制定规则,避免饥饿或死锁现象的发生。

信号量设置

定义互斥信号量数组chopstick[5]={1,1,1,1,1},用于对5个筷子的互斥访问。
哲学家按顺序编号为0-4,哲学家i左边筷子的编号为i,哲学家右边筷子的编号为(i + 1)%5。

  1. 可能死锁的算法
semaphore chopstick[5] = {1,1,1,1,1};
Pi() {
    do {
        P(chopstick[i]);
        P(chopstick[(i+1)%5]);
        eat;
        V(chopstick[i]);
        V(chopstick[(i+1)%5]);
        think;
    }while(1);
}
  1. 避免死锁
semaphore chopstick[5] = {1,1,1,1,1};
semaphore mutex = 1;//互斥访问桌子,一个人拿筷子时其他人都不能拿
Pi() {
    do {
        P(mutex);
        P(chopstick[i]);
        P(chopstick[(i+1)%5]);
        V(mutex);
        eat;
        V(chopstick[i]);
        V(chopstick[(i+1)%5]);
        think;
    }while(1);
}

吸烟者问题

  • 问题描述

假设一个系统有三个抽烟者进程和一个供应者进程。
每个抽烟者不停地卷烟并抽掉它,但要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。
三个抽烟者中,第一个拥有烟草,第二个拥有纸,第三个拥有胶水。
供应者进程无限地提供三种材料,供应者每次将两种材料放到桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,
并给供应者一个信号告诉已完成,此时供应者就会将另外两种材料放到桌上,如此重复(让三个抽烟者轮流地抽烟)。

  • 问题分析

关系分析。

供应者与三个抽烟者分别是同步关系。
由于供应者无法同时满足两个或以上的抽烟者,三个抽烟者对抽烟这个动作互斥(或由三个抽烟者轮流抽烟得知)。

整理思路。

显然这里有4个进程。供应者作为生产者向三个抽烟者提供材料。

信号量设置。

信号量offer1、offer2、offer3分别表示烟草和纸组合的资源、烟草和胶水组合的资源、纸和胶水组合的资源。
信号量finish用于互斥进行抽烟动作。

semaphore S[3] = {0,0,0};
semaphore finish = 0;
int random;
process pp() {
    while(1) {
        random = 任意一个整数随机数;
        random =random%3;
        V(S[random]);
        P(finish);
    }
}
process p0() {
    while(1) {
        P(S[0]);
        consume;
        V(finish);
    }
}
process p1() {
    while(1) {
        P(S[1]);
        consume;
        V(finish);
    }
}
process p2() {
    while(1) {
        P(S[2]);
        consume;
        V(finish);
    }
}

例题二

读者——写者问题

吸烟者问题

橘子——苹果问题

理发师问题

理发店有一位理发师、一把理发椅和n把等候理发的顾客坐的椅子,如果没有顾客,理发师便在理发椅上睡觉,一个顾客到来时,它必须叫醒理发师。如果理发师正在理发时又有顾客到来,则如果有空椅子可坐,就坐下来等待,否则就离开。

/*
等待位置是竞争资源设为n+1;
理发资源设为0;
*/
semphaore w_cheats=n+1;
semaphore b_cheats=0;
process barber() {
    while(true) {
        V(b_cheats);
        if(w_cheats==n+1)
            sleep();
    }
}
process customer() {
    if(b_cheats==0)
        leave();
    else {
        P(w_cheats);
        P(b_cheats);
        be_cut();
        V(w_cheats);
    }

}
/*

*/

int waiting=0;
int chairs=n;
semaphore customes,barbers,mutex;
customers=0;barber=0;mutex=1;

process barber() {
    while(true) {
        P(customer);
        P(mutex);
        waiting--;
        V(barbers);
        V(mutex);
        cut_hair();
    }
}

process customer() {
    P(mutex);
    if(waiting<chairs) {
        waiting++;
        V(customes);
        V(mutex);
        P(barbers);
        get_haircut();
    }
    else
        V(mutex);
}

生产与装配车间问题

缓冲区问题

有n个进程将字符逐个读入到一个容量为80的缓冲区中(n>1),当缓冲区满后,由输出进程Q负责一次性取走这80个字符。这种过程循环往复,请用信号量和P、V操作写出n个读入进程(P1,P2,…,Pn)和输出进程Q能正确工作的动作序列。

n个输入进程;
缓冲区为80;//等满
一个输出进程Q
semaphore empty=0,full=0,mutex=1;
int count=80;

void input(i=1,2,...,n) {
    while(true) {
        P(mutex);
        if(count==0) {
            V(full);
            P(empty);
        }
        else
            count--;
        V(mutex);
        
        输入;
    }
}

void output() {
    while(true) {
        P(full);
        
        读出所有数据;
        
        count=80;
        V(empty);
    }
}


var mutex,empty,full:semaphore;
count,in:integer;
buffer:array[0...79]of char;
mutex:=1;empty=80;full=0;
count=0;in=0;

process Pi(i=1,...,n)
begin
    L:
        读入一字符到x;
        P(empty);
        P(mutex);
        Buffer[in]=x;
        in=(in+1)%80;
        count++;
        if(count==80) {
            count=0;V(mutex);V(full);
        }
        else V(mutex);
    goto L;
end;  

process Q
begin
    while(true) {
        P(full);
        P(mutex);
        for(int j=0;j<80;j++)
            read buffer[j];
        in:=0;
        V(mutex);
        for(int j=0;j<80;j++)
            V(empty);
    }
    goto L;
end;  

哲学家进餐问题

有五个哲学家围坐在一圆桌旁,桌中央有一盘通心面,每人面前有一只空盘子,每两人之间放一把叉子。每个哲学家思考、饥饿、然后吃通心面。为了吃面,每个哲学家必须获得两把叉子,且没人只能直接从自己左边或右边取叉子。

  1. 可能死锁的算法
semaphore fork[5];
for(int i=0;i<5;i++)
    fork[i]=1;
cobegin
process philosopher_i() {
    while(true) {
        think();
        P(fork[i]);
        P(fork[(i+1)%5]);
        eat();
        V(fork[i]);
        V(fork[(i+1)%5]);
    }
}
coend
  1. 避免死锁

(1)至多允许四个哲学家同时取叉子

semaphore fork[5];
for(int i=0;i<5;i++)
    fork[i]=1;
semaphore room=4;
cobegin
process philosopher_i() {
    while(true) {
        think();
        P(room);
        P(fork[i]);
        P(fork[(i+1)%5]);
        eat();
        V(fork[i]);
        V(fork[(i+1)%5]);
        V(room);
    }
}
coend

(2)奇数号先取左边的叉子,偶数号先取右手边的叉子。

void philosopher(int i) 
{
    if i mod 2 == 0 then
    {
        P(fork[i]);
        P(fork[(i+1)mod 5]);
        eat();
        V(fork[i]);
        V(fork[(i+1)mod 5]);
    }
    else
    {
        P(fork[(i+1)mod 5]);
        P(fork[i]);
        eat();
        V(fork[(i+1)mod 5]);
        V(fork[i]);
    }
}

(3)一次性拿到两个叉子。

semaphore fork[5];
for(int i=0;i<5;i++)
    fork[i]=1;
semaphore mutex=1;
cobegin
process philosopher_i() {
    while(true) {
        think();
        P(mutex);
        P(fork[i]);
        P(fork[(i+1)%5]);
        V(mutex);
        eat();
        V(fork[i]);
        V(fork[(i+1)%5]);
    }
}
coend

农夫猎人问题

有一个铁笼子,每次只能放入一个动物。猎手向笼中放入老虎,农夫向笼中放入羊;动物园等待取笼中的老虎,饭店等待取笼中的羊。请用P、V操作原语写出同步执行的程序。

semaphore scage=1;
semaphore stiger=0;
semaphore ssheep=0;
void hunter() {
    while(true) {
        P(scage);
        将虎放入笼中;
        V(stiger);
    }
}
void peasant() {
    while(true) {
        P(scage);
        将羊放入笼中;
        V(ssheep);
    }
}
void zoo() {
    while(true) {
        P(stiger);
        将虎从笼中取出;
        V(scage);
    }
}
void hotel() {
    while(true) {
        P(ssheep);
        将羊从笼中取出;
        V(scage);
    }
}
void main() {
    parbegin(hunter,peasant,zoo,hotel);
}


/*
铁笼子=1;
羊=0;
老虎=0;
*/
void hunter() {
    while(true) {
        P(longzi);
        V(tiger);
    }
}
void farmer() {
    while(true) {
        P(longzi);
        V(sheep);
    }
}
void zoo() {
    while(true) {
        P(tiger);
    }
}
void cantoon() {
    while(true) {
        P(sheep);
    }
}

银行业务问题

某大型银行办理人民币存储业务,由n个储蓄员负责。每个顾客进入银行后先至取号机取一个号,并且在等待取找到空沙发坐下等着叫号。取号机给出的号码依次递增,并假定有足够多的空沙发容纳顾客。当一个存储员空闲下来,就叫下一个号。请用信号量和P,V操作正确编写储蓄员进程和顾客进程的程序。

n个储蓄员;
多个顾客m;
号码order=0;
process customer() {
    V(顾客);
    等待;m++;
    P(业务员);
    被处理;
    order++;
}
process serverI() {
    while(true) {
        P(顾客);
        P(next);
        处理业务;
        V(储蓄员);
    }
}


var customer_count,server_count,mutex:semaphore;
customer_count:=0;server_count:=n;
mutex:=1;
process customeri(i=1,2,...)
    begin
        take a number;
        P(mutex);
        等待区找到空沙发坐下;
        V(mutex);
        V(customer_count);
        P(server_count);
    end;
    
proces serversj(j=1,2,3,...)
    begin
        L:
            P(customer_count);
            P(mutex);
            被呼号顾客离开沙发走出等待区;
            V(mutex);
            为该号客人服务;
            客人离开;
            V(server_count);
         go to L;
     end;

黑白棋子问题

独木桥问题

机房问题

阅览室问题

你可能感兴趣的:(理论知识,操作系统)