【注意】代码在文末,以下为详细实验报告
【实验目的】
以读者写者问题为例,学习并熟悉Linux下进程通信、同步机制的具体实现方法,主要是了解并掌握信号量机制的使用方法,进一步熟悉Linux系统的相关指令的调用。
【实验内容】
在Linux环境下,创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应进行读写操作。用信号量机制分别实现读者优先和写者优先的读者-写者问题。 读者-写者问题的读写操作限制(包括读者优先和写者优先): 写-写互斥,即不能有两个写者同时进行写操作。 读-写互斥,即不能同时有一个线程在读,而另一个线程在写。 读-读允许,即可以有一个或多个读者在读。 读者优先的附加限制:如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。 写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态才能开始读操作。
【实验环境】(含主要设计设备、器材、软件等)
【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等)
一、数据结构
读者写者问题中,共定义了以下两个数据结构,分别是共享内存和readcount,详细定义内容如下所示
图1 共享内存数据结构
图2 readcount数据结构
二、算法描述
大致可以分为以下两个部分
1.读者优先
(1)初始化,产生共享内存并初始化共享内存,初始读取的进程为0个,将信号量集中的互斥读取信号量和文件资源信号量进行初始化,在后续读者写者进程运行时会对内容改写。
图3 初始化
(2)读者先通过p操作访问Readcount资源,通过Readcount是否为0判断该进程是否为第一个读者进程,若为第一个读者进程则取得文件资源信号量然后Readcount++,再通过v操作退出访问Readcount资源此后调用read()函数读取临界资源,在退出临界资源也与进入时一样,需判断是否为第一个读者进程,若是则需对file信号量进行v操作
图4 读者访问资源readcount
(3)写者进程在一开始的时候就需要对file信号量进行p操作以申请访问临界资源,在没有其他进程使用临界资源是才可以进入,从而实现了读者优先。
2.写者优先
在写者优先问题中,相较于读者优先,在读进程里增加一个信号量,让读进程与写进程公平竞争前需要先争夺该信号量实现写进程优先。
图5 写者优先主要代码
图6 写进程
3.原子操作
实现的P操作,通过得到信号量集的id并获取需要操作的信号量对其信号量进行-1操作。
图7 P操作
实现V操作,通过得到信号量集的id并获取需要操作的信号量对其进行+1操作。
图8 V操作
三、程序流程图
1.读者优先
图9 读者优先流程图
2.写者优先
图10 写者优先流程图
四、伪代码描述
1.读者优先
int readcount=0;
semaphore rmutex=1;
semaphore wmutex=1;
writer()
{
while(1)
{
P(rmutex);
…
Perform write operation
…
V(wmutex);
}
}
reader()
{
while(1)
{
P(rmutex);
if(count==0)
P(wmutex);
readcount++;
V(rmutex);
…
Perform read operation
…
P(rmutex);
readcount--;
if(count==0)
V(wmutex);
V(rmutex);
}
}
2.写者优先
int Writercount=0;
int Readercount=0;
semaphore wmutex=1;
semaphore rmutex=1;
semaphore wpmutex=1;
semaphore source=1;
writer()
{
while(1)
{
P(wmutux);
if(Writercount==0)
P(wpmuex);
Writercount++;
V(wmutux)
P(souce);
…
Perform write operation
…
V(souce);
P(wmutux);
Writercount--;
if(Writercount==0)
V(wpmutex);
V(wmutux);
}
}
reader()
{
while(1)
{
P(wpmutex);
P(rmutex);
if(Readercount==0)
P(souce);
Readercount++;
V(rmutex);
V(wpmutex);
…
Perform read operation
…
P(r_mutex);
Readercount--;
if(Readercount==0)
V(souce);
V(rmutex);
}
}
五、编译指令
//读者优先
$ gcc -o r_first.out r_first.c
$ ./r_first.out
//写者优先
$ gcc -o w_first.out w_first.c
$ ./w_first.out
图11 所有编译指令
六、运行结果
1.读者优先
图12 r_first.c运行结果
2.写者优先
图13 w_first.c运行结果
【实验结果或总结】(对实验结果进行相应分析,或总结实验的心得体会,并提出实验的改进意见)
在读者优先中,如果读者获得了访问权,那么当一个进程读完,另一个也可以开始进行读,但是如果写进程获得了访问权,而读者可以优先获得临界区的访问权,从而写进程被阻塞,进入阻塞队列,因此,在读者优先中,写进程只有等待没有读者访问临界区的时候,才能得到临界区的访问权,而此时读者在任意时候都可以得到临界区的访问权。 在写者优先中,如果读者获得了访问权,那么当一个进程读完,另一个也可以开始进行读,但是如果读进程获得了访问权,而写者可以优先获得临界区的访问权,从而读进程被阻塞,进入阻塞队列,等此时的读者刚执行完V操作时,那么写者就可以进程P操作继续执行了。因此,在写者优先中,我们需要增加一个信号量,让读进程与写进程公平竞争前需要先争夺该信号量实现写进程优先。 设想当一个读者在读数据,另一个读者也来访问,由于同时允许多个读者同时进行读操作,所以第二个读者也被允许进入,同理第三个读者也被允许进入。现在假设一个写者到来,由于写操作是排他的,所以它不能写数据,而是被阻塞。随后其他的读者到来,这样只要有一个读者活跃,随后而来的读者都被允许访问数据。这样的结果是只要有读者陆续到来,它们一来就被允许进入,而写者将一直被挂起直到没有一个读者为止。 这次作业让我加深了对于信号量机制以及进程同步的理解,也让学到了许多东西,只有理论与实践相结合,才能把知识学得更加牢固更加扎实。 最后,感谢倪福川老师以及其他同学给予我的帮助。
代码
#include
#include
#include
#include
#define SHM_SIZE (1024*1024)
#define SHM_MODE 0600
#define SEM_MODE 0600
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
#else
#define n 4
#define DELAY (rand() % 5 + 1)
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
#endif
int semid=-1,shmId=-1;
struct readcount
{
int read;
int l;
}*rdc;
//reader process
void reader(int i)
{
sleep(DELAY);
printf("This is process %d,and there are %d are reading\n",i,rdc->read);
}
//writer process
void writer()
{
sleep(DELAY);
printf("This is the writing process\n");
}
void wait(int semid,int i)
{
struct sembuf sb;
sb.sem_num = i;
sb.sem_op = -1;
sb.sem_flg = SEM_UNDO;
if(semop(semid,&sb,1)
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?