操作系统概念作业题(1~6章)

@操作系统概念作业题(1~6章)

第一章

1.资源利用问题在不同的操作系统中以不同的形式出现。请指出下面哪些资源必须被仔细地管理(请说明为什么)
(1). 主机系统或微型计算机
(2). 通过服务器连接的工作站
(3). 手持计算机
解答

(1) 主机系统是一类单台可支持8颗以上处理器的高端服务器系统,是包括大型硬件平台和操作系统的一体化方案。微型计算机是由大规模集成电路组成的,体积较小的电子计算机。以微处理器为基础,配以内存储器及输入输出接口电路和相应的辅助电路而构成的裸机。从定义上分析我们可以发现二者在以下资源方面需要被仔细地管理:内存,外存,CPU,I/O设备,网络

(2) 工作站是一种高端的通用微型计算机,也可以指终端机,所以以下资源方面需要被仔细地管理:内存和CPU

(3) 手持计算机就是指我们的笔记本电脑,由于笔记本电脑的体积有限,所以散热有限,这就使得我们在以下资源方面需要被仔细的管理:内存,网络,CPU

2.请结合存储层次体系说明为何软盘这种存储介质会消亡。
解答
(1) 存储层次结构中,随着层次降低,访问时间增加、价格更便宜、存储空间更大。软盘的的存储容量极低,存储容量量级为KB或者MB,并且访问时间长,与其他的存储介质来比,缺乏竞争力;
(2) 读取速度慢,由于软盘读取方式的局限,磁头在读写软盘数据时必须接触碟片,而不是像磁盘悬空读写,读取速度大概30KB/s左右,目前usb的实际速度也可达到30MB/s左右,所以就速度来说也必须得淘汰了。
(3) 软盘的存储稳定性较差,容易受到外界环境影响,如受热、受潮、多次读写,均使之寿命减少。

3.中断(Interrupt)有何作用? 陷阱(Trap)和中断有何区别? 用户程序能否有意地生成陷阱? 如果是,有什么目的?
解答
中断的作用:当CPU中断时,它暂停正在做的事并立即转到固定的位置去继续执行。(该固定位置通常是中断服务程序开始位置的地址)该程序执行完成后,CPU接着执行被中断的计算。这就是中断的作用。
区别
(1)陷阱是由软件引起的,中断是由硬件引起的。
(2)陷阱是异步的,中断是同步的。
(3)CPU处理中断的过程中会屏蔽中断,不接受新的中断直到此次中断处理结束。陷阱的发生并不屏蔽中断,可以接受新的中断。
用户可以有意地生成陷阱。其目的是为了将用户模式转入内核模式并把控制权移交给操作系统,使得用户程序可以调用内核函数或者相关硬件从而获得操作系统提供的服务。

4.直接内存访问(DMA)主要用于高速 I/O 设备以避免增加 CPU 的执行负荷。
(1) CPU 如何与设备协作完成数据传输?
(2) CPU 如何知道内存操作已经结束?
(3) 当DMA控制器在调度数据时,允许CPU执行其他程序。该进程与用户程序的执行会不会冲突?如是,说出将会导致何种冲突。

解答
1、设备驱动程序在设备控制器中装载适当的寄存器,在为I/O设备设置好缓冲,指针和计数器之后,设备控制器能在本地缓冲和内存之间传送一整块数据,每块只产生一个中断,从而告知CPU执行中断,提高效率
2、 CPU在每个指令结束之后会检测是否产生中断。当操作结束的时候,设备会发出中断指令,这个时候CPU会检测到中断指令,从而执行中断命令,结束内存操作。
3、会发生冲突。会导致设备和CPU都可以同时访问内存。那么在DMA控制器调度数据时,CPU执行别的程序访问内存时就可能发生进程切换。这个时候CPU需要抢夺设备使得自己数据存到内存之后,这就会影响CPU的速度,从而可能产生冲突。

5.请说明 CPU 中提供双模式(User, Monitor)有什么用途?并分别举例说明哪些指令属于特权指令,哪些属于非特权指令(不少于 10 种指令)。
解答
1、CPU双模式是CPU的一种保护措施,可以避免恶意程序直接调用一些特权指令对计算机进行破坏,提高安全性
2、
特权指令:①启动I/O设备指令;②测试I/O设备工作状态指令;③控制I/O设备动作指令;④对程序状态字的指令;⑤存取中断寄存器指令;⑥存取时钟寄存器指令;⑦清内存指令;⑧修改权限指令
非特权指令:①逻辑运算指令;②存数取数指令;③访管指令;④算术运算指令;⑤读时钟指令;

6.请阐述你对下图的认识。

解答
从开机开始,进入引导程序,将操作系统装入内存,初始化,同时初始化IdleLoop程序,无操作时一直执行IdleLoop程序,处于用户模式;当有事件发生时(I/O设备发生事件等)进入内核模式,操作系统处理事件,产生新的进程或者删除某些进程,然后将控制重新交给相应的用户程序并进入用户模式。只有在用户程序进行系统调用或者出现失败进入陷阱、或者定时器引发硬中断的时候会进入操作系统,进入内核模式。通过定时器中断进入操作系统的时候操作系统会对进程进行调度。

第二章

1.请指出执行系统调用时向操作系统传递参数的三种常用方式分别是什么,并分别说明在哪些情况下适合采用哪种方式。
解答
(1)、最简单的是通过寄存器来传递参数。一般是用于参数数量比较少的情况,例如中断的参数传递就是通过eax寄存器来传递,告诉操作系统需要执行什么类型的中断。
(2)、当参数数量比寄存器多,这些参数通常存在内存的块和表中,并将块的地址通过寄存器来传递。
(3)、参数也可通过程序放在或压入堆栈中,并通过操作系统弹出。这种方法不限制所传递参数的数量或者长度

2.操作系统为什么要将机制和策略区分开来?请查找文献并以实际操作系统中的案例来举例说明其好处。
解答
策略可能会随时间或位置而有所改变;在最坏情况下,每次策略改变都可能需要底层机制的改变。机制和策略区分能更好地实现资源分配。系统更需要通用机制,这样策略的改变只需要重定义一些系统参数。 当机制和策略分开时,策略可以随意地改变,但机制还是不能改变。这种安排提供了系统的灵活性
在最新的Solaris版本,调度由可加载的表控制。根据当前加载的表,系统可以是分时的、批处理的、实时的或公平分配的。制定调试机制的目的在于通过单个load-new-table命令得到巨大的策略改变。另一种极端的情况是Windows系统,其中机制和策略在系统中被编码以形成统一的系统风格。

3.操作系统采用微内核设计的主要优点是什么?用户程序和系统服务在微内核结构内如何相互影响?采用微内核设计的缺点又是什么?
解答
优点:便于扩充操作系统,所有的新服务可以在用户空间增加,因为不需要修改内核。当内核确实需要改变时,所做的改变也会很小,因为微内核本身很小。这样的操作系统很容易移植到不同的硬件平台设计。由于绝大多数服务作为用户而不是作为内核进程来运行的,因此微内核也就提供了更好的安全性和可靠性,如果一个服务器出错,那么操作系统的其他部分不受影响。
用户程序和系统服务通过使用进程的通信机制在微内核内相互作用,例如发送信息。这些信息由操作系统负责运送。
缺点:微内核系统的核心只实现了最基本的系统操作,这样内核以外的外部程序之间由于独立运行使得系统难以进行良好的整体优化。另外,进程间相互通信的开销也较单一内核系统大得多。

4.在第二章中介绍了一个从一个源文件向一个目标文件复制内容的程序。请用 Win32或 POSIX 的 API 写出这个 C 程序,并确保你的 C 程序中包括了所有必要的错误检测(如文件是否存在等)。
解答

#include <stdio.h>		//定义输入\输出函数
#include <stdlib.h>		//定义杂项函数及内存分配函数
#include <string.h>		//定义字符串处理函数
#include <sys/types.h>	//提供类型pid_t 和 size_t的定义
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>		//声明了整型的极限常量
#include <unistd.h>
#define BUFSIZE 8192

int main( int argc, char ** argv)
{
  if (argc!=3)
  {
    printf("\n usage: copy src dst\n");
    return -1;
  }
  int src, dst;
  char buf[BUFSIZE];
  int n;
  src=open(argv[1], O_RDONLY);
  dst=open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
  while ( ( n = read(src, buf, BUFSIZE) ) > 0)
  {
     if (write(dst, buf, n) != n)
           printf("write error!");
  }
  if (n<0)
     printf("read error !");
  close(src);
  close(dst);
  exit(0);
}

5.掌握系统级,观察并记录。一旦你正确地设计并实现了题目4中的程序,采用跟踪系统调用的工具来运行它,观察并记录相应的过程。(Linux 系统提供了 ptrace 工具,而Solaris 系统则采用 truss 或 dtrace 命令。在 MacOSX 中,dtrace 工具提供了类似的功能。)
解答
在这里插入图片描述
使用Linux下的strace跟踪程序来跟踪从源文件向目标文件拷贝内容过程中发生的系统调用。输入 strace -c -tt ./copyfile 统计每一次系统调用所执行的时间、次数和出错的次数等

其中 -tt 参数表示在输出中的每一行前加上时间信息,微秒级
输入strace -T -tt -e trace=file ./copyfile来跟踪从一个源文件向目标文件复制内容中与文件有关的系统调用;
-T参数表示显示每一次调用所消耗的时间;
-e trace=file表示只跟踪有关文件操作的系统调用

第三章

1.使用下图所示的程序,说明 LINE A 的输出是什么。为什么?

#include < sys/types.h >
#include < stdio.h >
#include <unistd.h >
int value =5;
int main()
{
	pid_t pid;
	pid = fork();
	if(pid == 0)
	{
		value += 15;
	}
	else if(pid > 0)
	{
		wait(NULL);
		printf("PARENT: value = %d", value );/*LINE A*/
		exit();
	}
}

解答
LINE A输出为PARENT:value = 5
通过fork()系统调用,可创建新进程,新进程通过复制原来进程的地址空间而成。该子进程除了PID与父进程不同之外,几乎完全与父进程相同,两者拥有相同却独立的地址空间。
父进程通过系统调用wait()来等待子进程的完成。当子进程完成时(通过显示或隐式调用exit()),父进程会从wait()调用处开始继续,并调用系统调用exit()以表示结束。当执行pid = fork()返回时,父进程value为5,子进程value值为20;子进程执行完毕,回到父进程,会打印出PARENT:value=5。

在这里插入图片描述

2.下面设计的优点和缺点分别是什么?分别从操作系统层面和用户层面来阐述。
• 同步和异步通信
• 自动和显式缓冲
• 复制传送和引用传送
• 固定大小和可变大小消息

解答

  • 同步和异步通信:
    同步通信,就是在发出一个功能调用时,在没有得到结果之前,该调用的就不返回。
    优点:在用户层次,同步通信保证了用户之间的同时同步性,确保了用户体验
    缺点:在系统层次,同步通信会因为等待反馈信息而一直占用内存和系统资源
    异步通信,在发出一个功能调用后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
    优点:在系统层次,异步通信是可以使系统尽可能高效率的进行不同的对象进行通信;
    缺点:在用户层次,在发出信息后不能立刻得到结果,甚至可能因为某种错误的发生而不能得到结果,影响用户体验
  • 自动或显式缓冲:
    自动缓冲提供了一个无限长度的队列,从而保证了发送者在复制消息时不会遇到阻塞,如何提供自动缓存的规范,一个方案也许能保存足够大的内存,但许多内存被浪费缓存明确指定缓存区的大小。
    优点:在用户层次,自动缓冲可以流畅的发送信息而不用担心被阻塞而造成的卡顿情况
    缺点:在系统层次,这样做会消耗或者浪费大量的系统资源和内存空间
    显示缓冲
    优点:在系统层面,这样只会使用一小部分内存空间,避免了系统资源的浪费
    缺点:采用显示缓冲可能使用户在发送信息时被阻塞等待一段时间。
  • 复制发送和引用发送:
    复制发送不允许接收者改变参数的状态,引用发送是允许的。
    引用发送允许的优点是它允许程序员写一个分布式版本的一个集中的应用程序。如,Java’s RMI公司提供两种发送,但引用传递一个参数需要声明这个参数是一个远程对象。
  • 固定大小和可变大小消息:
    由进程发送的消息可以是定长的或变长的。如果只能发送定长消息,那么系统级的实现十分简单,不过这一限制让编程任务更加困难。相反的,变长消息要求更复杂的系统级实现,但是编程任务变得更简单。

3.fibonacci序列是一组数:0,1,1,2,3,5,8,…,通常他可以表示为:
在这里插入图片描述

使用系统调用fork()编写一个c程序,使其在子程序中生成fibonacci序列,序列的号吗将在命令行中提供。例如,如果提供的是5,fibonacci序列中的前5个数将由子进程输出。退出程序前,父进程调用wait()调用来等待子进程结束。执行必要的错误检查以保证不会接受命令行传递来的负数号码。
解答

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc,char* argv[])
{
	pid_t pid;
	int i;
	int input;
	scanf("%d", &input);
	int f0,f1,f2;
	f0=0;
	f1=1;
	pid=fork();
	if(pid<0){
		printf("We need a nun-negative number");
		exit(-1);
	}
	else if(pid == 0)
	{
		printf("the number We input is %d\n",input);
		printf("0 1 ");
		for(i=2;i<input;i++){
			f2=f0+f1;
			f0=f1;
			f1=f2;
			printf("%d ",f2);
		}
	}
	else{
		wait(NULL);
		printf("end\n");
	}
	return 0;
}

运行结果如下:
在这里插入图片描述
4.请检索文献了解某一特定操作系统(如 Solaris,Windows 等)所提供的进程状态及其可能的状态转换关系,并与基本的进程状态转换图进行比较。
解答
在这里插入图片描述
在这里插入图片描述
这是linux系统的进程状态图,可以看到与基本的进程状态转换图基本一致,其中就绪状态没有改变;创建状态没有体现;运行状态没有改变,对应于占有cpu执行状态;阻塞状态分成了暂停,深度睡眠,浅度睡眠三个状态;停止状态对应于死亡状态。

5.请详细描述上下文的切换过程,并谈谈你对上下文切换的作用理解。
解答

  • 上下文切换是指,将CPU切换到另一个进程需要保存当前进程的状态并恢复另一个进程的状态。上下文就是内核重新启动一个被抢占的进程所需的状态。进程的上下文用进程的PCB(PCB属于操作系统的内核空间而不属于进程)表示,包括CPU寄存器的值、进程状态和内存管理信息等。进程在进行上下文切换时,需要保存的信息包括PC寄存器的值、CPU寄存器的值和堆栈指针,而不包括全局变量和局部变量
  • 上下文的切换过程: 首先保存当前运行在CPU中的任务的上下文(CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些CPU中的寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务;其中,保存的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。
  • 上下文切换的作用:
    1、挂起一个进程,将这个进程在cpu中的状态存储在内存中的某处
    2、在内存中检索下一个进程的上下文并将其在cpu中的寄存器中恢复
    3、跳转到程序计数器所指向的位置,以恢复该进程

6.请简述你对进程的理解,并分析进程与程序的区别和联系。
解答
想要理解进程,需要从进程的三个方面进行理解,即:映像、上下文/状态、内核数据结构(PCB)
1、映像:进程是可执行程序的映像,包含代码(文本段)、当前活动(程序计数器的值与寄存器的内容)、进程堆栈段(临时数据)、数据段(全局变量)、堆(动态分配的内存)。
2、上下文/状态:进程状态有五种,分别是新建、运行、等待、就绪、终止。
3、进程控制块(PCB):每个进程在os内用进程控制块表示,其中包含进程状态、程序计数器、cpu寄存器、cpu调度信息、内存管理信息、记账信息、I/O状态信息
进程与程序的区别:程序是被动实体,进程为活动实体(有一个程序计数器来表示下一个要执行的命令与相关资源集合),同一个程序可以产生多个进程。只有当一个可执行文件被装入内存,程序才能成为进程。

第四章

1.在包含多线程的进程中,下列那些对象可能被线程所共享?

  • 寄存器值
  • 堆内存
  • 全局变量
  • 栈内存
  • 局部变量
    解答
    线程之间可以共享堆内存和全局变量,但每个线程都有属于自己的一组寄存器值、栈内存和局部变量。(为了保证线程的独立运行)

2.使用多线程解决方案,在多处理器系统中可以比在单处理器系统中获得更好的性能吗? 为什么?
解答
一个多线程系统无法在多处理器系统上同时使用不同的处理器。操作系统只能看到一个单一的进程而不会调度在不同处理器上的不同进程的线程,因此多处理器系统执行多个用户线程是没有性能优势的。

3.考虑在多处理器系统中采用多对多线程模式编写的多线程程序,使程序中用户级线程数比系统中处理器数多。讨论下列情形的性能影响:

  • 分配给程序的内核线程数比处理器数少。
  • 分配给程序的内核线程数与处理器数相等。
  • 分配给程序的内核线程数比处理器数多,但少于用户线程数。
    解答
  • 当分配给程序的内核线程数比处理器数少时,一些处理器将仍处于空闲状态。因为调度图中只有内核线程的处理器,而不是用户线程的处理器。
  • 当分配给程序的内核线程数与处理器数相等时,那么有可能所有处理器将同时使用。然而,当一个内核块内的内核(因页面错误或同时援引系统调用)相应的处理器将闲置。
  • 当分配给程序的内核线程数比处理器数多,但少于用户线程数时,封锁一个内核线程并调出,换入另一个准备执行的内核线程。因此,增加多处理器系统的利用率。

4.请谈谈你对阿姆达尔定律的理解,并说明如何提高系统的加速比。
解答
该定律的主要思想是,当我们对系统的某个部分加速时,起对系统整体性能的影响取决于该部分的重要性和加速程度。
在这里插入图片描述
其中,a为并行计算部分所占比例,k为并行处理的个数。
当1-a=0时,(没有串行,只有并行)最大加速比s=n;
当a=0时,(只有串行,没有并行)最小加速比s=1;
当k→∞时,s → 1 /(1-a),即加速比的上限。

这就是 Amdahl 定律的主要观点:要想显著加速整个系统,必须提升全系统中相当大的部分的速度。即相当于增加并行性。

第五章

1.为什么对调度程序而言,区分CPU约束型进程和I/O约束型进程很重要?
解答
CPU约束型进程可以利用整个时间片,且不会做任何阻碍I/O操作的工作;另一方面,I/O约束型进程有在运行I/O操作前只运行很少数量的计算机操作的性质。这种进程一般不会使用很多的CPU。所以,通过给I/O约束型进程优先权和允许在CPU约束型进程之前运行,可以很好地利用计算机资源。

2.下面哪种调度算法会导致饥饿?

  • 先到先服务
  • 最短作业优先
  • 轮转法
  • 优先级
    解答
    最短作业优先和优先级会导致饥饿,因为对于优先级较低的作业来说,这两种算法都会使其无穷等待CPU,长期得不到调用,就会导致饥饿,也就是无穷阻塞问题。

3.考虑一个运行10个I/O约束任务和1个CPU约束任务的系统,假设I/O约束任务每进行1ms的CPU计算执行一次I/O操作,每个I/O操作花费10ms完成。假设上下文切换花费0.1ms,且所有的进程都是长运行任务。请计算下列条件下RR(时间片轮转)调度程序的CPU利用率?

  • 时间片为1ms
  • 时间片为10ms
    解答
    CPU的利用率= 所有进程使用CPU的时间/CPU的时间
    对于1ms的时间片,不论是哪个进程被调度,这个调度都会为每一次的上下文切换花费0.1ms的时间,CPU的利用率为1/1.1×100%=92%。
    对于10ms的时间片,I/O约束型任务会在使用完1ms时间片进行一次上下文切换。这个时间片要求在所有的进程间都走一遍,因此,10 × 1.1 + 10.1(每个I/O约束型任务执行为1ms,然后承担上下文切换的任务,而CPU约束型的任务执行10ms在承担一个上下文切换之前)。因此,CPU的利用率是20/21.1*100%=94%。

4.考虑下面一组进程

进程到达时间CPU区间长度(ms)优先级
P10103
P2131
P3243
P4314
P5452

(1)分别画出采用FCFS,SJF,抢占式优先级调度(数值越小,优先级越大;优先级相同时采用RR,时间片为1ms),RR(时间片=2ms)算法进行调度时的甘特图。
(2)计算上述调度算法下的平均周转时间。
(3)计算上述调度算法下的平均等待时间。
解答
FCFS
在这里插入图片描述
SJF(非抢占):在这里插入图片描述
SJF(抢占):

在这里插入图片描述

注意,对于长度相同的情况,我采用片长为1ms的RR
优先级:
在这里插入图片描述
RR:
在这里插入图片描述

平均周转时间:

周转时间FCFSSJF(非抢占)SJF(抢占)优先级RR
P11010232223
P212133311
P3151671512
P41582206
P5191910517
平均周转时间14.2ms13.2ms9ms13ms13.8ms

平均等待时间:

等待时间FCFSSJF(非抢占)SJF(抢占)优先级RR
P100131213
P2910008
P311123118
P41471195
P514145012
平均等待时间9.6ms8.6ms4.4ms8.4ms9.2ms

第六章

1.如果将 peterson 算法中的 flag[i] = true 与 turn = j 两条语句交换顺序,会导致求解临界区问题所需三个要求(互斥、有空让进、有限等待)中的哪些要求得不到满足?请举例并分析说明得不到满足的情况。
解答
如果顺序颠倒,总共会六种可能的语句组合情况:
在这里插入图片描述
如果仅仅从结果来看,似乎六种情况都满足三个条件:
在这里插入图片描述
但如果考虑中间能够进入临界区的情况,那么情况三和情况四将会不满足互斥条件,进程i与进程j将有可能同时进入临界区:

在这里插入图片描述
所以,会存在不满足互斥的情况,所以不可以。

2.试分析说明为何自旋锁(spinlocks)不适合单处理器系统但却常用于多处理器系统。
解答

  • 自旋锁(进程在其等待锁时还在运行)的缺点是忙等待,当一个进程位于其临界区内时,任何其它试图进入其临界区的进程都必须在其进入代码中连续地循环。在单处理器系统中,忙等待浪费了CPU时钟(这些时钟本来可以有效地为其他进程所使用)。
  • 自旋锁的优点就是,进程在等待锁时不会进行上下文切换,而上下文切换可能需要花费相当长的时间。因此,如果锁的占用时间短,自旋锁就可以变得非常有用。它常常用于多处理器系统中,这样一个线程在一个处理器自旋时,另一线程可在另一处理器上在其临界区内执行。

3.请用比较并交换指令(compare_and_swap())实现互斥锁机制。互斥锁包含的数据结构及函数如下所示,其中 available == 0 表示锁可用,available == 1 表示锁不可用。

typedef struct{
int available;
} lock ;
void acquire (lock *mutex) ;
void release (lock *mutex) ;

解答
在一个进程访问临界区时,其它进程不被允许进入临界区,因此引入了互斥锁机制用于在一个进程访问临界区时进行加锁操作,使得其它进程无法进入临界区,直到当前进程从临界区中返回并释放锁。
compare_and_swap()指令得定义如下:

int CompareAndSwap(int *value, int expected,int new_value)
{
	int temp=*value;
	if(*value==expected)
		*value=new_value;
	return temp;
}

用它实现互斥访问

void acquire(lock *mutex){
	if(mutex->available){//锁被占用
		add this process into waiting queue;//将当前进程加入到等待队列中
		block();//阻塞当前进程
	}
}
void release(lock *mutex){
	CompareAndSwap(mutex->available,1,0);
	remove a process P from waiting queue;//将进程P从等待队列中移出
	wakeup(P);//唤醒P进程
}

4.理发师问题:理发店里有一位理发师、一把理发椅和 n 把供等候理发的顾客坐的椅子。如果没有顾客,理发师便在理发椅上睡觉。当一个顾客到来时,它必须叫醒理发师。如果理发师正在理发时又有顾客到来,如果有空椅子可坐,就坐下来等待,否则就离开。请用信号量机制(wait(),signal())来解决上述问题。
解答
PV操作:对信号量进行相应操作
S:信号量
P(wait()操作):请求操作,相当于S=S-1;S>=0,进程继续进行;否则被挂起
V(signal()操作):释放操作,相当于S=S+1;S>0,进程被唤醒

首先定义custNum:当前顾客数量(用于判断后来者是否应该离开),mutex=1:互斥量(保证custNum信号量互斥)
确定同步关系:顾客和理发师之间有同步关系,用customer(初值为0)表示顾客是否准备好,用barber(初值为0)来表示理发师是否完成一次理发
值得注意的是:并非每个进程都需要while(1)循环,如果顾客剪完一次头发就走了,不可能马上再来剪。

在这里插入图片描述
操作系统概念1~8章作业答案https://download.csdn.net/download/weixin_43979304/15321012?spm=1001.2014.3001.5503

相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页