【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告

【注意】代码在文末,以下为详细实验报告

【实验目的】

  以读者写者问题为例,学习并熟悉Linux下进程通信、同步机制的具体实现方法,主要是了解并掌握信号量机制的使用方法,进一步熟悉Linux系统的相关指令的调用。

【实验内容】

  在Linux环境下,创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应进行读写操作。用信号量机制分别实现读者优先和写者优先的读者-写者问题。
  读者-写者问题的读写操作限制(包括读者优先和写者优先):
  写-写互斥,即不能有两个写者同时进行写操作。
  读-写互斥,即不能同时有一个线程在读,而另一个线程在写。
  读-读允许,即可以有一个或多个读者在读。
  读者优先的附加限制:如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。
  写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态才能开始读操作。

【实验环境】(含主要设计设备、器材、软件等)
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第1张图片

【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等)

一、数据结构

  读者写者问题中,共定义了以下两个数据结构,分别是共享内存和readcount,详细定义内容如下所示
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第2张图片

            图1 共享内存数据结构

【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第3张图片

            图2 readcount数据结构

二、算法描述

大致可以分为以下两个部分

1.读者优先

(1)初始化,产生共享内存并初始化共享内存,初始读取的进程为0个,将信号量集中的互斥读取信号量和文件资源信号量进行初始化,在后续读者写者进程运行时会对内容改写。
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第4张图片

            图3 初始化

(2)读者先通过p操作访问Readcount资源,通过Readcount是否为0判断该进程是否为第一个读者进程,若为第一个读者进程则取得文件资源信号量然后Readcount++,再通过v操作退出访问Readcount资源此后调用read()函数读取临界资源,在退出临界资源也与进入时一样,需判断是否为第一个读者进程,若是则需对file信号量进行v操作
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第5张图片

          图4 读者访问资源readcount

(3)写者进程在一开始的时候就需要对file信号量进行p操作以申请访问临界资源,在没有其他进程使用临界资源是才可以进入,从而实现了读者优先。

2.写者优先

  在写者优先问题中,相较于读者优先,在读进程里增加一个信号量,让读进程与写进程公平竞争前需要先争夺该信号量实现写进程优先。
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第6张图片

          图5 写者优先主要代码
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第7张图片

              图6 写进程

3.原子操作

  实现的P操作,通过得到信号量集的id并获取需要操作的信号量对其信号量进行-1操作。
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第8张图片

            图7 P操作

  实现V操作,通过得到信号量集的id并获取需要操作的信号量对其进行+1操作。
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第9张图片

            图8 V操作

三、程序流程图

1.读者优先
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第10张图片

          图9 读者优先流程图

2.写者优先
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第11张图片

          图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.读者优先

【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第12张图片
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第13张图片

          图12 r_first.c运行结果

2.写者优先
在这里插入图片描述
【操作系统实验】Linux环境下用进程实现读者写者问题——C语言完整代码+详细实验报告_第14张图片

          图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) < 0)
    {
     
        perror("Wait Failed");
        exit(1);
    }
}
void signal(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) < 0)
    {
     
        perror("Signal Failed");
        exit(1);
    }
}

void init() //initialization
{
     
    //share readcount
    if((shmId = shmget(IPC_PRIVATE,SHM_SIZE,SHM_MODE)) < 0)
    	{
     
    		perror("Create Chared Memory Failed");
    		exit(1);
    	}
    rdc = (struct readcount*)shmat(shmId,0,0);
    rdc->read= 0;
    semid = semget(IPC_PRIVATE,3,IPC_CREAT | 0666);
    if(semid < 0) 
    {
     
        perror("Create Semaphore Failed");
    	exit(1);
    }
    union semun sem;
    sem.val = 1;
    //exclusive read
    if(semctl(semid,0,SETVAL, sem) < 0)
    {
     
    	perror("Semctl Failed");
    	exit(1);
    }
    sem.val = 0;
    //readcount count
    if(semctl(semid,1,SETVAL, sem) < 0)
    {
     
    	perror("Semctl Failed");
    	exit(1);
    }
    sem.val = 1;
    //source
    if(semctl(semid,2,SETVAL, sem) < 0)
    {
     
    	perror("Semctl Failed");
    	exit(1);
    }
}

int main()
{
     
    init();
    int i=-1,child=-1;
    for(i=0;i<n;i++)
    {
     
        if((child = fork()) < 0)
    		{
     
    			perror("Fork Failed");
    			exit(1);
    		}
    		else if(child == 0)
    		{
     
    			printf("This is the kid %d , pid = %d , ppid = %d\n",i,getpid(),getppid());
    			while(1)
    			{
     
    				Wait(semid,0);
    				if(rdc->read==0)
                        Wait(semid,2);
    				rdc->read = rdc->read + 1;
    				Signal(semid,0);
    				reader(i);
    				Wait(semid,0);
                    rdc->read--;
                    printf("%d process have read\n",i);
                    if(rdc->read==0)
                        Signal(semid,2);
    				Signal(semid,0);
    				sleep(2);
    			}
    			break;
    		}
    }
    if(child > 0) //parent process
    	{
     
    		while(1)
    		{
     
    			Wait(semid,2);
    			writer();
    			Signal(semid,2);
    		}
    	}
    	return 0;
}

你可能感兴趣的:(操作系统,操作系统,读者写者,c++)