dtitle: OS记录型信号量-读者写者问题
date: 2021-04-09 19:06:16
tags: [信号量机制, OS, 读者写者问题, 记录型信号量]
categories: OS
写在最前
最近对 OS 这门课着了迷,hhh,拿起课本就是硬啃
用了一段时间领悟了记录型信号量的几个版本
这是对记录型信号量的一个应用,那么 pv操作( 即wait()和signal() )的具体代码描述 肯定是要烂熟于心的
在这里我就不写书上那么详细了
在这里我唠叨几句,pv操作虽然简简单单的这几行代码,但是非常具有思考性,很多细节只有自己想通了,才能将pv操作“玩弄于鼓掌之上”
改篇详细介绍一下pv操作 和 里面值得思考的细节
wait(S) {
S->value--;
if (S->value < 0)
block(S->list);
}
signal(S) {
S->value++;
if (S->value <= 0)
signal(S->list);
}
初始版本
为什么叫初始版本呢? 因为它不公平,对读者友好,对写者十分不友好
我们知道读者写者问题中 读读操作是可以同时进行的,唯独写操作不可以与任何操作同时进行
可以进行的操作 单独读 多个同时读 单独写 ;同时读写 和 多个同时写 是不允许的
在这个版本中只有当所有的读者都不再占用文件的时候 写进程才有机会访问文件,光说没什么卵用
下面分析代码,细致地了解到底是怎么个情况,我会将分析对应地写到注释里
着重分析初始版本,因为后面实现读者写者公平的版本 仅仅是对它的一个改良
代码
// readcount 表示当前有几个读进程正在读文件
int readcount = 0;
// rmutex 用于实现 读读进程 之间的互斥,保护readcount, 防止多个读进程同时访问/修改 readcount
// wmutext 用于实现 读写、写写之间的互斥,总之,一句话,用于实现某一个写进程和其它所有进程的互斥
semaphore rmutex = 1, wmutex = 1;
// 读进程
void Reader() {
do {
// 占用 readcount
// 可以理解为 给readcount "上锁"
wait(rmutex);
/*
当readcount == 0时, 说明 这个文件可能正在被写进程访问 或者是这个文件在此之前一直处于闲置
状态且写进程也无意争抢该文件,也有可能 于此同时写进程也想要访问这个文件, 两者将争夺该文件。
因此必须加上一个wait语句来保证某一个写进程和其它任意进程的互斥。
下面分析一下 当readcount == 0时, 这个进程执行wait语句之后可能的结果
我将结果分为两个大项,四个小项
- 抢到了文件(在抢的一瞬间文件空闲)
①文件空闲 且执行wait语句前没有写进程和它抢,它肯定是顺利地得到了文件。
wmutex值的变化:
wmutex 在wait语句执行前的值为 1 执行之后为 0
之后若再有写进程想要访问文件:
<1>若在signal(wmutex)语句执行之前 有一个写进程再想通过wait(mutex)来获得文件,
那么结果只能是 wmutex 值变为 -1 ,这个写进程被阻塞 进入阻塞队列,等待所有读进程
都读完之后,readcount变为0 ,执行signal(wmutex)语句,才会去唤醒这个写进程
(在此情况下,这个写进程在阻塞队列中必定处于队头),
顺带着说了 这也是对写进程的不公平之处。
<2>若又有一个读进程想要访问文件,请看第50-51行注释
②文件空闲 但于此同时有一个写进程也想通过wait语句争抢文件 对文件"上锁",
但是它先于写进程一步抢到了。
wmutex值的变化 以及之后再有进程想要访问文件的变化同上述第一条
- 没有抢到文件(在抢到之前,文件已经被写进程占了)
①文件不空闲 有一个写进程一直都在使用着文件。
wmutex 在wait执行前的值为m,(m <= 0), 执行后为 m-1,
为什么说wmutex原来值为一个<=0的数呢?
在这里又要分两种情况,在写进程开始写文件之后,这个读进程想要读文件之前的期间:
1. 没有其它进程想要访问文件,那么阻塞队列一定是空的,wmutex 值为0
2. 有其它的n个进程曾想要访问文件,结果肯定是它们都进入了阻塞队列,
wmutex值为 -n == m
②文件空闲 但同时,写进程也想通过wait语句争抢文件 对文件"上锁",
写进程抢先一步,得到了文件。
wmutex 在wait语句执行之前的值为 0, 之后值为 -1
*/
/*
当readcount != 0时, 说明现在有读进程正在读文件,并且第一个执行读操作的进程已经针对
写进程上了锁,因此直接令readcount++, 然后进行读操作,读完以后将readcount再减一
*/
/*
以上 其实我已经把整个的思路都给讲了,并且交代了为什么会对写进程不公平
还有几种简单情况我没有提的就是 上来就是写进程抢到了文件,在第37行注释中所述情况
也是把这个情况给包括了
*/
/*
像进程同步互斥、带pv操作的这种这种联系性很大的代码
真的不能说我要讲就只讲这一段 它们之间紧密联系
单独地理解一段代码很难领略到实质,我也是讲着讲着第一段就把整个的都给说出来了hhhh
也算是给广大后来者提供一种学习的方式吧 要善于整体感知
*/
if (readcount == 0) wait(wmutex);
readcount++;
// "释放" 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);
}
// 程序入口
void main() {
cobegin
Reader(); Writer();
coend
}