OS第6次实验报告:使用信号量解决进程互斥访问

  • 姓名:巫艳珍
  • 学号:201821121034
  • 班级:计算1812

1.读者-写者问题

(1)读者-写者问题描述如下情况:对象在多个线程之间共享,一些线程只读数据,一些线程只写数据。为保证写入和读取的正确性,操作限制:

  • 写-写互斥,即不能有两个写者同时进行写操作。  
  • 读-写互斥,即不能同时有一个线程在读,而另一个线程在写。 
  • 读-读允许,即可以有一个或多个读者在读。

(2)解决方案:读者优先或写者优先。

2.伪代码

(1)读者优先

当已经有线程在读数据的时候,其他读线程无需等待,而写线程需要等待所有正在进行的读操作之后才能执行。

semaphore rw=1;//实现对文件的互斥访问,表示当前是否有进程访问共享文件
int readcount=0;//记录访问文件的读进程数
semaphore mutex=1;//保证互斥访问
//写者
writer(){
  while(1){
     P(rw);//写文件前进行加锁
     写文件
     V(rw);//写文件之后进行解锁        
}  
} 
//读者
reader(){
    while(1){
        P(mutex);//读进程互斥访问readcount
        if(readcount==0)
            P(rw);//第一个读进程“加锁”,阻塞写者
        readcount++;//访问文件的读进程数加1
        V(mutex);
        读文件
        P(mutex);
        readcount--;
        if(readcount==0)
             V(rw);//解锁
        V(mutex);
}
}    

问题:只有所有读者都读完,写者才能写,当读者过多时,会导致写者出现“饿死”的情况

(2)写者优先

写者优先需要满足的要求:多个读者可以进行读;写者要互斥,只允许一个写者写,不能读者写者同时进行;写者优先于读者,一旦有写者,后续的读者必须等待,唤醒时优先考虑写者,直到最后的写者完成操作。

尽量满足写操作,不能并发,但可以排队,优先于等待的读线程获得执行权

int readcount=0,writecount=0;
5个信号量,mutex1,mutex2,mutex3,r,w
writer(){//写进程
    P(mutex2);
    writecount++;
    if(writecount==1)
        P(r);//第一个为写者,阻止后面的读者
    V(mutex2);
    P(w);
    写文件
    V(w);
    P(mutex2);
    writecount--;
   if(writecount==0) V(r);
   V(mutex2);
}
reader(){//读者进程
    P(mutex3);
    P(r);
    P(mutex1);
    readcount++;

 if(readcount==1)//第一个读者,互斥写者
        P(w);
    V(mutex1);
    V(r)
    V(mutex3);
    读文件
    P(mutex1);
    readcount--;
    if(readcount==0) V(w);//读者为最后一个,唤醒写者
    V(mutex1);
}

 

3.完整代码

#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/shm.h>
#include<semaphore.h>
#include<stdio.h>

int readcount=0,writecount=0; 
pthread_t p1,p2;
sem_t mutex1,mutex2,mutex3,r,w;//5个信号量 



void* writer(void* arg)
{
    int i = *(int*)arg;
    printf("进程%d想要写入\n",i);
    sem_wait(&mutex2);
    writecount++;
    if(writecount==1) //若第一个为写者,阻止后续的读者 
        sem_wait(&r);
    sem_post(&mutex2);
    sem_wait(&w);//写者互斥 
    printf("进程%d正在写入\n",i);
    sleep(4);
    printf("进程%d完成写入\n",i);    
    sem_post(&w);
    sem_wait(&mutex2);
    writecount--;
    if(writecount==0) sem_post(&r);//所有的写者写完才能让P(r)的读者readcount增加 
    sem_post(&mutex2);
}

void* reader(void* arg){
    int i = *(int*)arg;
    printf("进程%d想要读取\n",i);
    sem_wait(&mutex3);
    sem_wait(&r);
    sem_wait(&mutex1);
    readcount++;
    if(readcount==1)//若第一个为读者,互斥写者 
        sem_wait(&w);
    sem_post(&mutex1);
    sem_post(&r);
    sem_post(&mutex3);
    printf("进程%d正在读取\n",i);
    sleep(2);
    printf("进程%d完成读取\n",i);    
    sem_wait(&mutex1); 
    readcount--;
    if(readcount==0)//读者运行完释放写者,读写互斥 
        sem_post(&w);
    sem_post(&mutex1);
    
}

int main(int argc,char *argv[]){
    int ret;
    int pro_num=atoi(argv[1]);//输入进程数 
    sem_init(&r,0,1);//初始化信号量 
    sem_init(&w,0,1);
    sem_init(&mutex1,0,1);
    sem_init(&mutex2,0,1);
    sem_init(&mutex3,0,1);
    readcount=0;
    writecount=0;
    if(argc!=2){
        fprintf(stderr,"wrong\n");
        return 1;
    }
    printf("共有%d个进程\n",pro_num);
    int n[pro_num+1];
    for(int i=0;i<pro_num;i++){
        int s=rand()%2+1;
        int f=rand()%2;
        sleep(s);
        n[i]=i;
        if(f){
            ret=pthread_create(&p1,NULL,reader,&n[i]);
            if(ret!=0){
                printf("error to create reader!\n");
                exit(1);
            }
        }
        else{
            ret=pthread_create(&p2,NULL,writer,&n[i]);
            if(ret!=0){
                printf("error to create writer!\n");
                exit(1);
            }
        }
    }
    pthread_join(p2,NULL);
    pthread_join(p1,NULL);
    sleep(20);
    return 0;
    
}
  • 信号量常用函数:

    sem_init:初始化信号量sem_t,初始化的时候可以指定信号量的初始值,以及是否可以在多进程间共享。

    sem_wait:一直阻塞等待直到信号量>0。

    sem_post:使信号量加1。

  • pthread_join()函数:以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。

4.结果截图及解释

(1)结果截图:

 

(2)解释:实现的是写者优先

一共十个进程,进程2等待进程1完成写入后进行读取,进程3想要读取,能够实现与进程2同时读取,进程4想要读取,进程2和3读取完毕进程4开始写入,此时进程5、6、7想要读取,只有等进程4完成写操作才能读取,当进程9和进程10均想要写入时,不能同时进行写操作,待进程9完成写操作时进程10才写入。

5.遇到的问题

 在编译的时候出现报错collect2:error:Id returned 1 exit status

解决:编译时连接库,在编译命令后添加:-lpthread -ldl -lm

 

 

 

posted @ 2020-05-28 20:56  王尔  阅读(308)  评论(0编辑  收藏  举报