操作系统——第二章 (二)

38、信号量机制是一种进程同步思想,保证进程的先后执行顺序。需要掌握的有整形信号量、记录型信号量、AND型信号量。信号量集不用掌握,了解思想就可以。还有利用信号量解决实际问题,比较有代表性的是生产者与消费者问题、哲学家进餐问题、读者写者问题。
39、信号量是一种特殊的数据结构,有P操作和V操作,PV操作都是原子操作,P操作时让代表资源数目的变量-1,V操作是让代表资源数目的变量+1。P操作类似与申请使用资源,V操作就是释放资源。
40、整形信号量就是基本的PV操作,因为在P操作中需要判断while(S <= 0); ,所以不满足让权等待原则,每次执行都需要耗费CPU一个时间片的时间。
41、记录型信号量从定义看,是一种特殊的结构体就比较好理解了。该结构体包含两个内容,一个是代表资源数目的value,一个是因为申请该资源不够引起阻塞的进程,以链表的形式存在。P操作的主要思想是,不管资源数量如何,先执行value-1操作,如果value<0,意味着资源不够,就需要把进程添加到结构体中阻塞队列里边。V操作的主要思想是先value+1,如果value<=0,说明仍然有阻塞的进程,就要从结构体阻塞进程队列里取出一个来,唤醒他。这种方法比整形信号量进步的地方在于,满足让权等待,但是最重要的一个问题是可能会引起死锁。例如AB两个进程,A进程连续申请两种临界资源PA(s1),PA(s2),B进程同样连续申请两种互斥资源PB(s2),PB(s1)。倘若按照PA(s1)--PB(s2)--PA(s2)--PB(s1)的顺序执行,会在PA(s2)这里发生死锁,s2资源已经被B进程申请分配,这里又得不到释放,A进程会因为得不到s2,而不释放s1,B进程也得不到s1,就死锁了。
42、所以,基于记录型信号量的特点,又引出了AND型信号量。你不是怕发生死锁吗?那一次性申请所有的需要用的资源,也就是P操作。如果发现第一个不够的资源(不满足Si>=1),就把进程添加到这个资源的阻塞队列里,如果所有资源都够,就把所有资源一次性分配到位。V操作是把所有的Si+1,并把因为Si不够的进程唤醒。
43、但是仍旧有问题,在AND型信号量中,一次P操作只能申请一种资源里的一个资源,假设要申请多个,就需要多次P操作,明显不方便,所有就引出了信号量集。基于AND型信号量,信号量集的主要思想是分配所需要的全部资源。
44、P操作和V操作一定要成对出现,缺少P操作,不能保证互斥访问临界资源,缺少V操作不能释放资源,不能让其他进程申请资源导致进程一直处于阻塞状态,不会被唤醒。并且PV操作按照一定顺序执行才行,否则可能会引起系统混乱。
45、利用信号量机制解决生产者问题,生产者问题前边已经有描述。先要分析问题,不允许消费者进程从一个空缓冲区中取产品、不允许生产者进程往一个已满且还没被取走的缓冲区中投放产品,要想实现要用到两个信号量。还有,不允许生产的时候消费,不允许消费的时候生产,也就是将缓冲池看成一个临界资源,这需要一个信号量,总共需要三个信号量。假设初始缓冲池是空的,用empty=n代表剩余空的位置数量,用full = 0代表已占有的位置数量,用mutex = 1代表生产和消费互斥的访问缓冲池。
生产者:
wait(empty);
wait(mutex);
Buffer[in]=nextp;
in=(in+1) % n;
signal(mutex);
signal(full);
消费者:
wait(full);
wait(mutex);
nextc=buffer[out];
out=(out+1) % n;
signal(mutex);
signal(empty);
一定要注意申请资源的顺序,生产者先申请empty,再申请mutex,消费者先申请full,在申请mutex。假设交换生产者和消费者中的申请empty和mutex,就有可能产生死锁。例如,缓冲池满了,empty=0了,生产者先申请mutex,再申请empty=0,因为empyt已经为0 了,这一步的P操作会一直处于判断状态,而消费者因为生产者已经占用缓冲池了,就不能够执行到向缓冲池取走内容,也就是发生了死锁。
释放顺序比较好理解,后申请的,先释放,类似于回家一样,先开大门,后开客厅门,走到时候是先锁客厅门,后索大门。因为我们一直把互斥资源这种东西比作锁,那就按照锁去理解。
46、记两种资源的申请顺序比较麻烦,有没有好的方式?那就是AND型信号量,对于生产者来说,empty和mutex一起申请,一起释放,对于消费着来说,full和mutex一起申请,一起释放,就没有刚才的死锁问题了。
47、哲学家进餐问题,五个哲学家,五支筷子,一个人吃饭同时需要两只筷子,如何解决这个问题。可以利用记录型信号量,把五个筷子当作五个临界资源,哲学家先申请左边的筷子,再申请右边的筷子,只有同时获得左右两边的筷子,才能够进餐,进餐完毕后释放筷子这个资源。假设五个哲学家同时申请左边的筷子,又不能得到右边的筷子,这样会发生死锁。如何解决?可以加一些限制条件,比如只允许最多四个人同时申请进餐;规定奇数的先申请左边的筷子,再申请右边的筷子,偶数的先申请右边的筷子,再申请左边的筷子 ,这样肯定会有一个哲学家会获得两只筷子,问题就解决了。

48、哲学家进餐问题关键在于同时获得两只筷子,同时是AND型信号量的特点,所以用AND型信号量就没有刚才诸多复杂的问题。
49、读写问题。捋清楚读和写的关系,读的时候不能写,写的时候不能读,所以读写时互斥的,写和写之间也是互斥的,读和读之间并不互斥,允许同时读,所以读(写)和写之间需要一个互斥信号量wmutex。此外,还需要考虑一个问题,如何判断是否有正在读的进程?可以利用一个count代表读进程的数量,如果没有正在读的,也就是count=0,读之前要申请wmutex,如果有正在读的,也就是count>1,说明前边已经申请过wmutex,不用再次申请,只需要count+1就可以,读结束之后执行count-1。但是其中还有问题,刚才提到的判断是否有读的进程,不是原子操作,为了保证count不被同时访问,将count视为临界资源,另找rmutex作为操作count的临界资源信号量。
把上述思路写成读写进程代码:
semaphore rmutex = 1, wmutex = 1;
int readcount = 0;
void Reader(){
do{
wait(rmutex);
if(readcount == 0) wait(wmutex);
readcount++;
signal(rmutex);

perform read operation;

wait(rmutex);
readcount--;
if(readcount == 0) signal(wmutex);
signal(rmutex);
}while(TRUE)
}
void Writer(){
do{
wait(wmutex);
perform write operation;
signal(wmutex);
}while(TRUE)
}

50、进程的通信分为高级通信和低级通信,前边提到的信号量就是低级通信。缺点是效率低(传递数据量小)、对用户不透明(需要用户自己去实现,增加了用户的工作量)。如果为了实现进程之间传递消息,可以利用OS提高到高级通信方式,相对来讲,可以增加传递的数据量,可以对用户透明(不需要用户实现,直接调用现成的原语就可以)。
51、进程的高级通信包括:共享存储器系统、管道通信、消息传递系统、客户机-服务器系统。
52、共享存储器系统:共享数据结构的通信方式、共享存储区通信方式。两种方式类似,区别是共享数据结构的方式共享的数据量比较小,共享存储区的方式共享的数据量比较大,原理都是共享同一片存储空间,可以类比成全局变量。如果从数据量来说,共享数据结构的通信方式就属于低级通信,共享存储区通信方式属于高级通信。
53、管道通信:基于文件系统,单向的。读进程和写进程有同步和互斥关系。同步是说,写进程将管道写满或写到一定数据量,就阻塞,读进程取走数据后再把写进程唤醒,反之同理。互斥是说,写的时候不能读,读的时候不能写。
54、消息传递系统:有直接通信和间接通信。直接通信是调用OS原语,send直接给目标进程发送消息、receive直接接收来自发送进程发送的消息,在send和recive中包含了消息内容、目标进程识别码。可以利用send和recieve原语解决生产者和消费者问题,更简单。间接通信需要依靠一个中间实体,这个中间实体就是信箱。信箱相当于是一个中间桥梁,将发送者将信息先放到信箱里,接收者再从信箱里取走消息。只有自己才能信箱的创建和撤销也是要通过原语实现的,所以创建信箱必须要给出信箱的名字、信箱是否被共享的属性。信箱的属性是说信箱是否是共享的,所以信箱要分为私有信箱(由用户创建,拥有者可以取,其他进程只能向里写。类似于自己家门口的奶箱,送奶的可以往里放,只有自己有钥匙可以拿走奶,任何人都拿不走奶。所以是单向的。)、公用信箱(由系统创建,所有进程都能够用,所以是双向的)、共享信箱(由进程创建,相当于和邻居共同使用一个奶箱,都能够打开箱子取走奶)
55、客户机-服务器系统:计算机网络中会学到,不是我们这门课的重点。
56、为什么引入进程?实现并发操作,提高系统效率。为什么引入线程?进程有缺点,进程的切换在时间和空间上消耗较大,将进程部分角色功能用线程代替,例如被CPU调度的功能。
57、将进程和线程区分开,能够明白两者之间的联系、区别。a、从被CPU调度来看,线程是被CPU调度的最小单位,线程不拥有资源(其实有一丢丢),线程会使用所属进程的资源,所以进程的资源是拥有的线程共享的。这样的话同一进程里的线程切换涉及不到进程的切换,时间空间开销就少。b、并发性:线程也可以并发,不同进程之间的线程也可以并发,但是会设计进程的切换。c、拥有资源:线程不拥有资源(其实有一丢丢),线程会使用所属进程的资源,所以进程的资源是拥有的线程共享的。c、独立性:独立性可以理解为关联性,同一进程中的线程独立性比不同进程中的线程低。d、系统开销:进程的创建要分配资源,建立PCB,线程的创建不需要分配资源,只用创建线程的TCB,进程的终止要回收资源PCB,线程只要回收TCB就可以,所以线程的创建、终止、切换时间空间开销都要少。e、支持多处理机系统:一个CPU可以调度一个线程,多个CPU可以调度同一进程下的多个线程,所以在多处理机情况下,可以把一个进程分为若干个线程并行执行,提高进程的执行效率。

原文地址:https://www.cnblogs.com/lgwdx/p/14580263.html