📄 article_3327.txt
字号:
2读者2写者问题编程实现
一、 设计思路
主程序使用shmget()函数来获得一个共享内存区的IPC标识符,shmat()函数将获得
的共享内存区附加到主程序上。共享内存区存放一个share的数据结构如下:
typedef struct
{
int buff[512]; /*buffer for writer and reader*/
int in; /*indicate the writers where to put num in
the buffer*/
int out; /*indicate the readers where to get num from
the buffer*/
int val; /*the current value of num which need to be
put into the buffer*/
long long sum; /*the final sum*/
}share;
接下来使用fork()创建4个子进程,分别为2个写者和2个读者。读写者需要同步互
斥。因此使用semget()函数来获取信号量,并需要定义P、V操作。由于子进程可继
承父进程所附加的共享内存区,因此不需要再在各个子进程中附加共享内存,只需
在传递参数时将共享内存地址传给子进程即可。最后需要记得回收资源。
二、 具体实现
这次我实现了2个程序,一个为in_turn.c,实现了2写者轮流写,2读者轮流读;另
一个为not_in_turn.c,是以类似锁的方式实现的,即只保证一个时刻只有一个读
者和一个写者,不保证轮流性。2种方法一个比较大的区别是前者是6个信号量,而
后者是4个信号量。这里主要说下in_turn.c,即轮流的实现。
一开始需要在主程序中获取和附加共享内存,并对共享内存中的数值进行初始化。
接下来获取信号量:
int in1_id,in2_id,out1_id,out2_id,empty_id,full_id;
定义P、V操作(初始化sembuf结构):
struct sembuf P,V;
P.sem_num=0;
P.sem_op=-1;
P.sem_flg=0;
V.sem_num=0;
V.sem_op=1;
V.sem_flg=0;
然后需要对信号量赋初值(调用semctl( )函数):
初值赋1的信号量:in1_id,out1_id
初值赋0的信号量:in2_id,out2_id,full_id
初值赋512(共享内存中数组大小)的信号量:empty_id
这样赋值的原因是要给writer1一开始就运行的机会,当它执行一次write操作流程
如下:
1. P(empty_id)
2. P(in1_id)
3. Write num to buff
4. V(in2_id)
5. V(full_id)
其他writer2、reader1、reader2流程可类推。这里写的数值使用共享内存
中的share结构中的val。Val从1开始,每个写进程将它写到buff[in]后将它
递增1,读进程每次将buff[out]中的值读出,加入自己的一个sum中,无论
是读还是写进程都循环50000次,读进程在循环结束后将自己计算的sum
加入共享内存的sum中,这样就能保证从1加到100000了。
由于需要写到100000,而只有512的缓存,所以需要循环利用,使用了对512进
行取模运算,这样就能实现了。
主进程在获取完共享内存和信号量之后就是调用fork()函数了,主程序需要调用4
次fork( ),然后调用waitpid等待他们运行结束,以免产生僵死进程。子进程在结
束前应该调用shmdt( )将共享内存区解除。父进程在等待所有的子进程返回后也需
要调用shmdt(),还要释放所有的信号量(调用semctl)。
三、 心得与体会
这次作业耗费的时间还是蛮多的,主要不是设计方案问题,而是一些细节
上问题很多,比如一开始的时候对共享内存的使用不是很了解,进行了多次实验,
然后是信号量上的一个问题困扰了我很久,就是PV操作的sem_flag的置位问题。一
开始参考其他资料将其置位SEM_UNDO,孰不知此置位方式在某个子进程结束时,相
应的操作将被取消。这样会导致其他子进程被阻塞。设置此标志位是为了防止进程
在没有释放共享资源就退出时,内核将代为释放。后来将它改为0,程序就正确了。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -