Linux多进程读者和写者问题,读者--写者----问题
1.问题分析
实现读者写者(Reader-Writer
Problem)问题是⼀个经典的并发程序设计问题,是经常出现的⼀种同步问题。所谓读者写者问题,是指保证⼀个writer进程必须与其他进程互斥地访问共享对象的同步问题。
读者写者问题可以这样的描述,有⼀写者和⼀读者,写者在写同⼀本书,读者也在读这本书,多个读者可以同时读这本书,但是,只能有⼀个写者在写书,并
且,读者必写者优先,也就是说,读者和写者同时提出请求时,读者优先。当读者提出请求时需要有⼀个互斥操作,另外,需要有⼀个信号量S来当前是否可操作。
1.1 问题详细描述
有⼀个被许多进程共享的数据区,这个数据区可以是⼀个⽂件,或者主存的⼀块空间,甚⾄可以是⼀组处理器寄存器。有⼀些只读取这个数据区的进程(reader)和⼀些只往数据区中写数据的进程(writer)。以下假设共享数据区是⽂件。这些读者和写者对数据区的操作必须满⾜以下条件:读—读允许;读—写互斥;写—写互斥。这些条件具体来说就是:
(1)任意多的读进程可以同时读这个⽂件;
(2)⼀次只允许⼀个写进程往⽂件中写;
(3)如果⼀个写进程正在往⽂件中写,禁⽌任何读进程或写进程访问⽂件;
(4)写进程执⾏写操作前,应让已有的写者或读者全部退出。这说明当有读者在读⽂件时不允许写者写⽂件。
Reader和Writer的同步问题分为读者优先、弱写者优先(公平竞争)和强写者优先三种情况,它们的处理⽅式不同。
对于读者-写者问题,有三种解决⽅法:
1、读者优先
除了上述四个规则外,还增加读者优先的规定,当有读者在读⽂件时,对随后到达的读者和写者,要⾸先满⾜读者,阻塞写者。这说明只要有⼀个读者活跃,那么随后⽽来的读者都将被允许访问⽂件,从⽽导致写者长时间等待,甚⾄有可能出现写者被饿死的情况。
2、写者优先
除了上述四个规则外,还增加写者优先的规定,即当有读者和写者同时等待时,⾸先满⾜写者。当⼀个写者声明想写⽂件时,不允许新的读者再访问⽂件。
3、⽆优先
除了上述四个规则外,不再规定读写的优先权,谁先等待谁就先使⽤⽂件。
1.2 设计的⽬的和要求linux下的sleep函数
通过对操作系统内核实现代码的阅读、修改、设计,理解和掌握复杂的操作系统的⼯作原理。
通过研究Linux的线程机制和信号量实现读者写者(Reader-Writer)问题并发控制。
实验条件要求:每⼈⼀台与Linux主机联⽹的Windows主机,普通⽤户权限。
2.设计思想
创建⼀个包含n
个线程的控制台进程。⽤这n
个线程来表⽰n个读者或写者。每个线程按相应测试数据⽂件的要求,进⾏读写操作。请⽤信号量机制分别实现读者优先和写者优先的读者-写者问题。对于这个问题我们可以有如下的两个思路:
将所有的读者和所有的写者分别放进两个等待队列中,当读允许时就让读者队列释放⼀个或多个读者,当写允许时,释放第⼀个写者操作。
读者优先:
如果没有写者正在操作,则读者不需要等待,⽤⼀个整型变量readcount记录当前的读者数⽬,⽤于确定是否释放写者线程,(当
readcout=0 时,说明所有的读者都已经读完,释放⼀个写者线程),每个读者开始读之前都要修改readcount,为了互斥的实现对readcount 的修改,需要⼀个互斥对象Mutex来实现互斥。
另外,为了实现写-写互斥,需要⼀个临界区对象
write,当写者发出写的请求时,必须先得到临界区对象的所有权。通过这种⽅法,可以实现读写互斥,当readcount=1 时,(即第⼀个读者的到来时,),读者线程也必须申请临界区对象的所有权。当读者拥有临界区的所有权,写者都阻塞在临界区对象write上。当写者拥有临界区对象所有权时,第⼀个判断完readcount==1 后,其余的读者由于等待对readcount的判断,阻塞在Mutex上!
写者优先:
写者优先和读者优先有相同之处,不同的地⽅在:⼀旦有⼀个写者到来时,应该尽快让写者进⾏写,如果有⼀个写者在等待,则新到的读者操作不能读操作,为此添加⼀个整型变量writecount,记录写者的数⽬,当writecount=0时才可以释放读者进⾏读操作!
为了实现对全局变量writecount的互斥访问,设置了⼀个互斥对象Mutex3。
为了实现写者优先,设置⼀个临界区对象read,当有写者在写或等待时,读者必须阻塞在临界区对象read上。
读者除了要⼀个全局变量readcount实现操作上的互斥外,还需要⼀个互斥对象对阻塞在read这⼀个过程实现互斥,这两个互斥对象分别为mutex1和mutex2。
我选择是设计思路是读者优先,当读者和写者同时要访问⽂件的时候,读者可以访问,写者要处于等待状态。
2.1设计思想描述
我的设计思路⼤致如下:
优先级:
读者优先(⼀个读者试图进⾏读操作时,如果有其他写者在等待进⾏写操作或正在进⾏写操作,他要等待该写者完成写操作后才开始读操作)。
将所有的读者和所有的写者分别放进两个等待队列中,当读允许时就让读者队列释放⼀个或多个读者,当写允许时,释放第⼀个写者操作。读者写者问题的定义如下:有⼀个许多进程共享的数据区,这个数据区可以是⼀个⽂件或者主存的⼀块空间;有⼀些只读取这个数据区的进程(Reader)和⼀些只往数据区写数据的进程(Writer)。
1、临界区(Critical
Section)是⼀段独占对某些共享资源访问的代码,在任意时刻只允许⼀个线程对共享资源进⾏访问。如果有多个线程试图同时访问临界区,
那么在有⼀个线程进⼊后其他所有试图访问此临界区的线程将被挂起,并⼀直持续到进⼊临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达
到⽤原⼦⽅式操作共享资源的⽬的。
临界区在使⽤时以CRITICAL_SECTION结构对象保护共享资源,并分别⽤EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放⼀个临界区。所⽤到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使⽤,⽽且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作⽤,共享资源依然有被破坏的可能。
2、互斥对象
创建互斥对象
CreateMutex(NULL,FALSE,"mutex_for_readcount");
参数含义如下:
NULL表⽰创建带有默认安全性的内核对象;
FALSE表⽰该互斥对象没有被任何线程所拥有;
mutex_for_readcount是为内核对象赋予名字;
ReleaseMutex(h_Mutex); 释放互斥信号。
对资源具有访问权的线程不再需要访问此资源⽽要离开时,必须通过ReleaseMutex()函数来释放其拥有的互斥对象。
3、定义线程结构:
struct ThreadInfo
{ int
Threadhao;
char
ThreadClass;
double ThreadStartTime;
double ThreadRunTime;
};
此结构⽤来存放线程的信息,四个成员变量依次表⽰线程序号、线程类别、线程开始时间、线程读写持续时间。
4、创建读者线程
CreateThread(NULL,0,\(LPTHREAD_START_ROUTINE)(R_ReaderThread),
\&thread_info[i],0,&thread_ID);
参数含义如下:
NULL表⽰创建带有默认安全性的内核对象;
0表⽰新读者线程拥有⾃⼰的堆栈,使⽤缺省⼤⼩:1MB;
(LPTHREAD_START_ROUTINE)(R_ReaderThread)表⽰新读者线程执⾏的线程函数的地址;
&thread_info[i]表⽰在线程启动执⾏时将该参数传递给读者线程函数;
0表⽰读者线程创建后可以⽴即进⾏调度;
&thread_ID表⽰CreateThread使⽤这个地址来存放系统分配;
给新读者线程的I D。
5、等待函数:
WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1)
等待函数可使线程⾃愿进⼊等待状态,直到⼀个特定的内核对象变为已通知状态为⽌
参数含义如下:
n_thread表⽰线程数量;
h_Thread是指向线程对象句柄的数组的指针;
ture表⽰:在所有线程对象变为已通知状态之前,该函数将不允许调⽤线程运⾏;
参数 -1
告诉系统,调⽤线程愿意永远等待下去(⽆限时间量),直到该进程终⽌运⾏。
6、程序由两部分组成:
读者-写者模块:包括系统调⽤接⼝,读者-写者活动描述主程序。系统接⼝主要功能是通过管道向⽗进程发送系统调⽤命令,并读取⽗进程送来的返回值。读者-写者活动程序根据临界资源的共享,互斥原则编制,具体见源程序。
主控模块:主控模块实现系统初始化系统调⽤命令接收与解释执⾏,系统调⽤功能的实现(包括信号量机制),及读者-写者活动过程记录与显⽰。
7、程序流程如下:
1.读取输⼊参数⽂件;
2.创建互斥量和信号量;
3.创建线程(挂起状态);
4.恢复线程运⾏;
5.等待所有线程运⾏结束, 退出。
具体说明:
1.各读/写者到达时间不同, ⽤长短不同的睡眠时间表⽰;
2.各读/写者访问临界资源的持续时间长短不⼀, 随机⽣成,
⽤不同的睡眠时间表⽰;
2.2 系统平台以及使⽤语⾔
我的课程设计实验是在与Linux主机联⽹的Windows主机(普通⽤户权限)上⾯运⾏的,我选择是是C语⾔,但是由于编程能⼒和在Linux下⾯的操作能⼒悠闲,所以我最后没有成功,但是在VC下⾯运⾏很成功,所以我写的报告的情况都是在windows7下⾯,编译器是Visual
C++ 6.0,我的结果也是在VC下⾯运⾏的结果。
3.详细的算法描述
信号量机制是⽀持多道程序的并发操作系统设计中解决资源共享时进程间的同步与互斥的重要机制,⽽读者写者问题则是这⼀机制的⼀个经典范例。
与记录型信号量解决读者—写者问题不同,信号量机制它增加了⼀个限制,即最多允许RN个读者同时
读。为此,⼜引⼊了⼀个信号量L,并赋予初值为RN,通过执⾏wait(L,1,1)操作,来控制读者的数⽬,每当有⼀个读者进⼊时,就要执⾏wait(L,1,1)操作,使L的值减1。当有RN个读者进⼊读后,L便减为0,第RN+1 个读者要进⼊读时,必然会因wait(L,1,1)操作失败⽽堵塞。对利⽤信号量来解决读者—写者问题的描述如下:
Var RN
integer;L,mx:semaphore: =RN,1;
Begin
Parbegin
Reader
:begin
Repeat
Swait(L,1,1);
Swait(mx,1,0);
.
Perform reader operation;
Ssignal(L,1);
Until false;
End
Writer
:begin
Repeat
Swait(mx
,1,1,l,RN,0);
Perform writer
operation;
Ssignal(mx,1);
Until
false;
End
Parend
End
其中,Swait(mx,1,0)语句起着开关作⽤,只要⽆Writer进程进⼊些,mx=1,reader进程就都可以进⼊读。但是要⼀旦有Writer进程进⼊写时,其MX=0,则任何reader进程就都⽆法进⼊读。Swait(mx ,1,1,l,RN,0)语句表⽰仅当既⽆Write进程在写(mx=1),⼜⽆reader进程在读(L=RN)时,writer进程才能进⼊临界区写。
4.源程序和详细的注释
#include
#include
#include
#define MAX_NUM
100
#define
LINESIZE
80
struct rwparm
{
int
seq;
int
rwtype;
float
arvtime;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论