Linux多进程实现读写者问题:读优先、写优先、公平排队实现

1. 任务简介

一个数据文件或记录可被多个进程共享,我们把只要求读该文件的进程称为 reader进稈,其他进程称为writer进程。允许多个进程同时读一个共享对象,因为读操作不会使数据文件混乱。但不允许一个writer进程和其他reader进程或writer进程同时访回共享对象,因为这种访问将会引起混乱。所谓读者-写者问题(reader-writer problem)是指保证一个writer进程必须与其他进程互斥地访问共享对象的向步问题。读者-写者问题常被用手测试新同步原语。

读者-写者问题被提出后,就一直被用手测试几乎所有的新同步原语。该问题有多个变种它们都与优先级有关。最为简单的通常被称为 “第一”读者-写者问题。该问题要求没有读者蛋要保持等待,除非有一个写者已被允许使用共享对象,换言之,如果有读者在访问对象则不管有没有写者在等待,后续读者都可以进行读操作。“第二”读者-写者问题要求:一旦写者就绪,那么写者会尽可能快地执行写操作。换言之,如果有一个写者在等待访问对象,那么就不会有新读者开始读操作。对这两个问题的解答都有可能导致饥饿。第一种情况下,写者可能饥饿;第二种情况下,读者可能饥饿。

本实验要求利用Linux多进程实现读写者问题。

2. 思路分析

我们分析题目中的同步和互斥关系:

2.1 同步关系

本题中并不存在需要特别关注的同步关系

2.2 互斥关系

  • 当写者进程执行写操作时,其他的写进程无法写
  • 当写者进程执行写操作时,读者进程无法读
  • 当读者进程执行读操作时,写着进程无法写

在这里插入图片描述

2.3 整体思路

这个问题中只有互斥关系,单相较于哲学家问题,其互斥的实现更为复杂,主要要考虑读写者进程对于文件的互斥访问。根据 “2.实验任务,我们按照读写进程的优先顺序,可将读写者问题分为 “第一”读者-写者问题“第二”读者-写者问题。针对以上两个问题,我们分别采取读优先写优先的策略。此外,由于两种问题都有可能会产生饥饿,我们针对饥饿问题给出了第三种解决方案,采取读写公平法

下面我们针对以上三种问题的实现思路进行解析:

  • “第一”读者-写者问题: 设置一个信号量reader-writer mutex,用于实现读者进程和写者进程的互斥访问。另外,设置一个变量readerCount用于记录读者进程的数目,当readerCount=0时,第一个读者进程给reader-writer mutex上锁,保证写者进程无法同时执行写操作;当readerCount>0时,读者进程可以直接进入,保证不同的读者进程可以同时执行读操作。当且仅当所有的读操作执行完毕时才会给reader-writer mutex解锁,从而保证了读优先的策略。
  • “第二”读者-写者问题: 设置一个信号量reader-writer mutex,用于实现读者进程和写者进程的互斥访问。另外,设置一个变量writerCount用于记录写者进程的数目,当writerCount=0时,第一个写者进程给reader-writer mutex上锁,保证写者进程无法同时执行写操作;当readerCount>0时,读者进程可以直接进入,但由于写操作无法同时操作,所以又需要一个信号量write mutex,实现对于文件的写互斥和排队等待。当且仅当所有的写操作执行完毕时才会给reader-writer mutex解锁,从而保证写优先的策略。
  • 读写公平法:第一”读者-写者的基础上,另外设置一个信号量mutex2用于标识读者和写者访问文件的机会。当写者进入时,任何读者无法取得该机会;当读者进行读操作时,先给mutex2上锁,然后释放,再开始读取文件,由此可以保证多个读进程可以同时读,同时,释放mutex2后由于readerCount已经上锁,故写者无法进行写操作,从而保证了读者进程执行读操作时,写着进程无法写。这样无论写进程还是读进程想要进入,都需要进行排队,服务的原则满足先到先得,较为公平。

程序具体流程如下图所示:

  • 读者优先流程图:

在这里插入图片描述

  • 写者优先流程图:

在这里插入图片描述

  • 公平调度流程图:

在这里插入图片描述

3. 代码实现

3.1 头文件

首先,我们包含实现问题所需的头文件:

#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/ipc.h>

3.2 预定义和数据结构

  • 我们采用共同协商关键字SEMKEYSHMKEY的方法使得不同进程间可以取得同一个信号量和共享内存
  • 定义了一个结构体Buffer用于模拟缓冲区的读写操作
#define SEMKEY 123
#define SHMKEY 456
#define BUFNUM 50
#define SEMNUM 4

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/*   union   semun   is   defined   by   including   <sys/sem.h>   */ 
#else 
/*   according   to   X/OPEN   we   have   to   define   it   ourselves   */ 
union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};
#endif

struct Buffer
{
    int length, writerCount, readerCount;
    char buffer[BUFNUM];
};

3.3 初始化函数

  • 我们利用协商好的SEMKEY生成一个信号量集,其中第1个信号量为reader-write,表示读写锁,用于控制读写互斥访问;第2个信号量为mutex,标识对于进程计时器count的互斥访问;第3个信号量为writer mutex,控制写进程间对于缓冲区的互斥访问(写优先策略中将会使用);第4个信号量为mutex2,标识读者和写者访问文件的机会,并引导读写进程进行公平排队(读写公平策略中将会使用)
  • 利用协商好的SHMKEY生成一个共享内存集,用于存放结构体Buffer
  • 我们利用*returnSemId*returnShmId**returnShm三个指针来返回初始化的参数

具体实现如下:

void Initialize(int *returnSemId, int *returnShmId, struct Buffer **returnShm)
{
    int semId = -1, shmId = -1, values[SEMNUM] = {1, 1, 1, 1};

    /*  semSet[0]: reader-writer mutex, initial value 1
        semSet[1]: mutex, initial value 1
        semSet[2]: writer mutex, initial value 1    
        setSet[3]: mutex2, inital value 1*/

    semId = semget(SEMKEY, SEMNUM, IPC_CREAT | 0666);
    if(semId == -1)
    {
        printf("semaphore creation failed!\n");
        exit(EXIT_FAILURE);
    }

    int i = 0;
    union semun semUn;
    for(i = 0; i < SEMNUM; i ++)
    {
        semUn.val = values[i];
        if(semctl(semId, i, SETVAL, semUn) < 0)
        {
            printf("semaphore %d initialization failed!\n", i);
            exit(EXIT_FAILURE);
        }
    }

    shmId = shmget(SHMKEY, sizeof(struct Buffer), IPC_CREAT | 0666);
    if(shmId == -1)
    {
        printf("share memory creation failed!\n");
        exit(EXIT_FAILURE);
    }

    void *temp = NULL;
    struct Buffer *shm = NULL;
    temp = shmat(shmId, 0, 0);
    if(temp == (void *) -1)
    {
        printf("share memory attachment failed!\n");
        exit(EXIT_FAILURE);        
    }
    shm = (struct Buffer *) temp;

    shm -> length = 0;
    shm -> writerCount = 0;
    shm -> readerCount = 0;
    shm -> buffer[0] = '\0';

    *returnSemId = semId;
    *returnShmId = shmId;
    *returnShm = shm;
}

3.4 PV操作

给定信号量集的semId以及待操作的信号量下标semNum,其P操作和V如下所示:

void SemWait(int semId, int semNum)
{
    struct sembuf semBuf;
    semBuf.sem_num = semNum;
    semBuf.sem_op = -1;
    semBuf.sem_flg = SEM_UNDO;
    if(semop(semId, &semBuf, 1) == -1)
    {
        printf("semaphore P operation failed!\n");
        exit(EXIT_FAILURE);
    }
}

void SemSignal(int semId, int semNum)
{
    struct sembuf semBuf;
    semBuf.sem_num = semNum;
    semBuf.sem_op = 1;
    semBuf.sem_flg = SEM_UNDO;
    if(semop(semId, &semBuf, 1) == -1)
    {
        printf("semaphore V operation failed!\n");
        exit(EXIT_FAILURE);
    }
}

3.5 读写操作

  • 对于读操作,我们将对共享内存buffer进行增加、删除、修改三种之一的操作,以此来模拟真实的文件写操作情况;

  • 对于写操作,我们将对共享内存buffer的内容进行打印,以此来模拟真实的文件读操作情况;

void Write(struct Buffer *shm)
{
    char ch, ch1, ch2;
    int mode = rand() % 3;
    /*  mode 0: append
        mode 1: delete
        mode 2: modify  */
    
    // appended when empty
    if(mode != 0 && shm -> length == 0) mode = 0;
    // deleted when full
    else if (mode == 0 && shm -> length == BUFNUM - 1) mode = 1;

    switch (mode)
    {
    case 0:

        ch = 'A' + rand() % 26;
        printf("writer %d: appended %c into file:\t\t", getpid(), ch);
        shm -> buffer [shm -> length ++] = ch;
        shm -> buffer [shm -> length] = '\0';
        break;
    
    case 1:

        ch = shm -> buffer [-- shm -> length];
        printf("writer %d: removed %c from file:\t\t", getpid(), ch);
        shm -> buffer [shm -> length] = '\0';
        break;        
        
    case 2:

        ch1 = shm -> buffer [shm -> length - 1];
        ch2 = 'A' + rand() % 26;
        printf("writer %d: modified %c into %c in the file:\t", getpid(), ch1, ch2);
        shm -> buffer [shm -> length - 1] = ch2;
        break;

    default:

        printf("writer %d: done nothing", getpid());
        break;
    }

    printf("|%s|\n", shm -> buffer);
}

void Read(struct Buffer *shm)
{
    printf("Reader %d: read the file:\t\t\t", getpid());
    printf("|%s|\n", shm -> buffer);
}

注意:当缓冲区为空时,我们规定写进程只可以进行添加操作;当缓冲区为满时,我们规定写进程只能进行删除或修改操作。

3.6 “第一”读者-写者

设置一个信号量reader-writer mutex,用于实现读者进程和写者进程的互斥访问。另外,设置一个变量readerCount用于记录读者进程的数目,当readerCount=0时,第一个读者进程给reader-writer mutex上锁,保证写者进程无法同时执行写操作;当readerCount>0时,读者进程可以直接进入,保证不同的读者进程可以同时执行读操作。当且仅当所有的读操作执行完毕时才会给reader-writer mutex解锁,从而保证了读优先的策略。具体实现如下:

void Writer1(int semId, struct Buffer *shm)
{
    do{
        // wait reader-writer mutex
        SemWait(semId, 0);
        // write
        Write(shm);
        // signal reader-writer mutex
        SemSignal(semId, 0);

        sleep(random() % 2);

    }while(1);
}

void Reader1(int semId, struct Buffer *shm)
{
    do{
        // wait mutex
        SemWait(semId, 1);
        // wait reader-writer mutex
        if(shm -> readerCount ++ == 0) SemWait(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        // read
        Read(shm);

        // wait mutex
        SemWait(semId, 1);
        // signal reader-writer mutex
        if(-- shm -> readerCount == 0) SemSignal(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        sleep(random() % 2);

    }while(1);
}

3.7 “第二”读者-写者

置一个信号量reader-writer mutex,用于实现读者进程和写者进程的互斥访问。另外,设置一个变量writerCount用于记录写者进程的数目,当writerCount=0时,第一个写者进程给reader-writer mutex上锁,保证写者进程无法同时执行写操作;当readerCount>0时,读者进程可以直接进入,但由于写操作无法同时操作,所以又需要一个信号量write mutex,实现对于文件的写互斥和排队等待。当且仅当所有的写操作执行完毕时才会给reader-writer mutex解锁,从而保证写优先的策略。具体实现如下:

void Writer2(int semId, struct Buffer *shm)
{
    do{
        // wait mutex
        SemWait(semId, 1);
        // wait Reader-writer mutex
        if(shm -> writerCount ++ == 0) SemWait(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        // wait writer mutex
        SemWait(semId, 2);
        // write
        Write(shm);
        // signal writer mutex
        SemSignal(semId, 2);

        // wait mutex
        SemWait(semId, 1);
        // signal Reader-writer mutex
        if(-- shm -> writerCount == 0) SemSignal(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        sleep(random() % 2);

    }while(1);
}

void Reader2(int semId, struct Buffer *shm)
{
    do{
        // wait reader-writer mutex
        SemWait(semId, 0);
        // read
        Read(shm);
        // signal reader-writer mutex
        SemSignal(semId, 0);

        sleep(random() % 2);

    }while(1);
}

3.8 读者-写者公平策略

第一”读者-写者的基础上,另外设置一个信号量mutex2用于标识读者和写者访问文件的机会。当写者进入时,任何读者无法取得该机会;当读者进行读操作时,先给mutex2上锁,然后释放,再开始读取文件,由此可以保证多个读进程可以同时读,同时,释放mutex2后由于readerCount已经上锁,故写者无法进行写操作,从而保证了读者进程执行读操作时,写着进程无法写。这样无论写进程还是读进程想要进入,都需要进行排队,服务的原则满足先到先得,较为公平。具体实现如下:

void Writer3(int semId, struct Buffer *shm)
{
    do{
        // wait mutex2
        SemWait(semId, 3);
        // wait reader-writer mutex
        SemWait(semId, 0);
        // write
        Write(shm);
        // signal reader-writer mutex
        SemSignal(semId, 0);
        // signal mutex2
        SemSignal(semId, 3);

        sleep(random() % 2);

    }while(1);
}

void Reader3(int semId, struct Buffer *shm)
{
    do{
        // wait mutex2
        SemWait(semId, 3);
        // wait mutex
        SemWait(semId, 1);
        // wait reader-writer mutex
        if(shm -> readerCount ++ == 0) SemWait(semId, 0);
        // signal mutex
        SemSignal(semId, 1);
        // signal mutex2
        SemSignal(semId, 3);

        // read
        Read(shm);

        // wait mutex
        SemWait(semId, 1);
        // signal reader-writer mutex
        if(-- shm -> readerCount == 0) SemSignal(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        sleep(random() % 2);

    }while(1);
}

3.9 主函数

主函数负责对变量的初始化,产生读者-写者进程,以及最后的资源回收。

int main(int argc, char *argv[])
{
    int semId = -1, shmId = -1, i=0;
    int processNum = atoi(argv[2]);
    if(processNum <= 0) processNum = 1;
    struct Buffer *shm = NULL;

    Initialize(&semId, &shmId, &shm);
    for(i = 0; i < 2 * processNum; i ++)
    {
        pid_t pid = fork();
        if(pid < 0)
        {
            printf("fork failed!\n");
            exit(EXIT_FAILURE);
        }
        else if(pid == 0)
        {
            sleep(1);
            if(i % 2 == 0)
            {
                printf("writer process %d created\n", getpid());
                // Writer1(semId, shm);
                // Writer2(semId, shm);
                Writer3(semId, shm);           
            }
            else
            {
                printf("reader process %d created\n", getpid());
                // Reader1(semId, shm);
                // Reader2(semId, shm);
                Reader3(semId, shm);    
            }
            return 0;
        }
    }
    getchar();
    Destroy(semId, shmId, shm);
    return 0;
}

注意:getchar()函数使得父进程接收到一个字符之后,回收系统资源,结束其产生的子进程。

3.10 实验代码

完整实验代码如下:

#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/ipc.h>


#define SEMKEY 123
#define SHMKEY 456
#define BUFNUM 50
#define SEMNUM 4

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/*   union   semun   is   defined   by   including   <sys/sem.h>   */ 
#else 
/*   according   to   X/OPEN   we   have   to   define   it   ourselves   */ 
union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};
#endif

struct Buffer
{
    int length, writerCount, readerCount;
    char buffer[BUFNUM];
};

void Initialize(int *returnSemId, int *returnShmId, struct Buffer **returnShm)
{
    int semId = -1, shmId = -1, values[SEMNUM] = {1, 1, 1, 1};

    /*  semSet[0]: reader-writer mutex, initial value 1
        semSet[1]: mutex, initial value 1
        semSet[2]: writer mutex, initial value 1    
        setSet[3]: mutex2, inital value 1*/

    semId = semget(SEMKEY, SEMNUM, IPC_CREAT | 0666);
    if(semId == -1)
    {
        printf("semaphore creation failed!\n");
        exit(EXIT_FAILURE);
    }

    int i = 0;
    union semun semUn;
    for(i = 0; i < SEMNUM; i ++)
    {
        semUn.val = values[i];
        if(semctl(semId, i, SETVAL, semUn) < 0)
        {
            printf("semaphore %d initialization failed!\n", i);
            exit(EXIT_FAILURE);
        }
    }

    shmId = shmget(SHMKEY, sizeof(struct Buffer), IPC_CREAT | 0666);
    if(shmId == -1)
    {
        printf("share memory creation failed!\n");
        exit(EXIT_FAILURE);
    }

    void *temp = NULL;
    struct Buffer *shm = NULL;
    temp = shmat(shmId, 0, 0);
    if(temp == (void *) -1)
    {
        printf("share memory attachment failed!\n");
        exit(EXIT_FAILURE);        
    }
    shm = (struct Buffer *) temp;

    shm -> length = 0;
    shm -> writerCount = 0;
    shm -> readerCount = 0;
    shm -> buffer[0] = '\0';

    *returnSemId = semId;
    *returnShmId = shmId;
    *returnShm = shm;
}

void Write(struct Buffer *shm)
{
    char ch, ch1, ch2;
    int mode = rand() % 3;
    /*  mode 0: append
        mode 1: delete
        mode 2: modify  */
    
    // appended when empty
    if(mode != 0 && shm -> length == 0) mode = 0;
    // deleted when full
    else if (mode == 0 && shm -> length == BUFNUM - 1) mode = 1;

    switch (mode)
    {
    case 0:

        ch = 'A' + rand() % 26;
        printf("writer %d: appended %c into file:\t\t", getpid(), ch);
        shm -> buffer [shm -> length ++] = ch;
        shm -> buffer [shm -> length] = '\0';
        break;
    
    case 1:

        ch = shm -> buffer [-- shm -> length];
        printf("writer %d: removed %c from file:\t\t", getpid(), ch);
        shm -> buffer [shm -> length] = '\0';
        break;        
        
    case 2:

        ch1 = shm -> buffer [shm -> length - 1];
        ch2 = 'A' + rand() % 26;
        printf("writer %d: modified %c into %c in the file:\t", getpid(), ch1, ch2);
        shm -> buffer [shm -> length - 1] = ch2;
        break;

    default:

        printf("writer %d: done nothing", getpid());
        break;
    }

    printf("|%s|\n", shm -> buffer);
}

void Read(struct Buffer *shm)
{
    printf("Reader %d: read the file:\t\t\t", getpid());
    printf("|%s|\n", shm -> buffer);
}

void ShmDestroy(int semId, struct Buffer * shm)
{
    if(shmdt(shm) < 0)
    {
        printf("share memory detachment failed!\n");
        exit(EXIT_FAILURE);
    } 
    if(shmctl(semId, IPC_RMID, 0) < 0)
    {
        printf("share memory destruction failed!\n");
        exit(EXIT_FAILURE);        
    }
}

void SemWait(int semId, int semNum)
{
    struct sembuf semBuf;
    semBuf.sem_num = semNum;
    semBuf.sem_op = -1;
    semBuf.sem_flg = SEM_UNDO;
    if(semop(semId, &semBuf, 1) == -1)
    {
        printf("semaphore P operation failed!\n");
        exit(EXIT_FAILURE);
    }
}

void SemSignal(int semId, int semNum)
{
    struct sembuf semBuf;
    semBuf.sem_num = semNum;
    semBuf.sem_op = 1;
    semBuf.sem_flg = SEM_UNDO;
    if(semop(semId, &semBuf, 1) == -1)
    {
        printf("semaphore V operation failed!\n");
        exit(EXIT_FAILURE);
    }
}

void SemDestroy(int semId)
{
    union semun semUn;
    if(semctl(semId, 0, IPC_RMID, semUn) < 0)
    {
        printf("semaphore destruction failed!\n");
        exit(EXIT_FAILURE);
    }
}

void Destroy(int semId, int shmId, struct Buffer *shm)
{
    SemDestroy(semId);
    ShmDestroy(shmId, shm);
    printf("destruction finished! exit\n");
}

void Writer1(int semId, struct Buffer *shm)
{
    do{
        // wait reader-writer mutex
        SemWait(semId, 0);
        // write
        Write(shm);
        // signal reader-writer mutex
        SemSignal(semId, 0);

        sleep(random() % 2);

    }while(1);
}

void Reader1(int semId, struct Buffer *shm)
{
    do{
        // wait mutex
        SemWait(semId, 1);
        // wait reader-writer mutex
        if(shm -> readerCount ++ == 0) SemWait(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        // read
        Read(shm);

        // wait mutex
        SemWait(semId, 1);
        // signal reader-writer mutex
        if(-- shm -> readerCount == 0) SemSignal(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        sleep(random() % 2);

    }while(1);
}

void Writer2(int semId, struct Buffer *shm)
{
    do{
        // wait mutex
        SemWait(semId, 1);
        // wait Reader-writer mutex
        if(shm -> writerCount ++ == 0) SemWait(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        // wait writer mutex
        SemWait(semId, 2);
        // write
        Write(shm);
        // signal writer mutex
        SemSignal(semId, 2);

        // wait mutex
        SemWait(semId, 1);
        // signal Reader-writer mutex
        if(-- shm -> writerCount == 0) SemSignal(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        sleep(random() % 2);

    }while(1);
}

void Reader2(int semId, struct Buffer *shm)
{
    do{
        // wait reader-writer mutex
        SemWait(semId, 0);
        // read
        Read(shm);
        // signal reader-writer mutex
        SemSignal(semId, 0);

        sleep(random() % 2);

    }while(1);
}

void Writer3(int semId, struct Buffer *shm)
{
    do{
        // wait mutex2
        SemWait(semId, 3);
        // wait reader-writer mutex
        SemWait(semId, 0);
        // write
        Write(shm);
        // signal reader-writer mutex
        SemSignal(semId, 0);
        // signal mutex2
        SemSignal(semId, 3);

        sleep(random() % 2);

    }while(1);
}

void Reader3(int semId, struct Buffer *shm)
{
    do{
        // wait mutex2
        SemWait(semId, 3);
        // wait mutex
        SemWait(semId, 1);
        // wait reader-writer mutex
        if(shm -> readerCount ++ == 0) SemWait(semId, 0);
        // signal mutex
        SemSignal(semId, 1);
        // signal mutex2
        SemSignal(semId, 3);

        // read
        Read(shm);

        // wait mutex
        SemWait(semId, 1);
        // signal reader-writer mutex
        if(-- shm -> readerCount == 0) SemSignal(semId, 0);
        // signal mutex
        SemSignal(semId, 1);

        sleep(random() % 2);

    }while(1);
}

int main(int argc, char *argv[])
{
    int semId = -1, shmId = -1, i=0;
    int processNum = atoi(argv[2]);
    if(processNum <= 0) processNum = 1;
    struct Buffer *shm = NULL;

    Initialize(&semId, &shmId, &shm);
    for(i = 0; i < 2 * processNum; i ++)
    {
        pid_t pid = fork();
        if(pid < 0)
        {
            printf("fork failed!\n");
            exit(EXIT_FAILURE);
        }
        else if(pid == 0)
        {
            sleep(1);
            if(i % 2 == 0)
            {
                printf("writer process %d created\n", getpid());
                // Writer1(semId, shm);
                // Writer2(semId, shm);
                Writer3(semId, shm);           
            }
            else
            {
                printf("reader process %d created\n", getpid());
                // Reader1(semId, shm);
                // Reader2(semId, shm);
                Reader3(semId, shm);    
            }
            return 0;
        }
    }
    getchar();
    Destroy(semId, shmId, shm);
    return 0;
}

4. 实验结果

我们通过gcc编译器编译源程序reader_writer.c,生成目标文件reader_writer

在这里插入图片描述

我们从控制台输入命令$ ./reader_writer -n 4 ,来模拟4个读写者同时工作的情况:

4.1 “第一”读者-写者

通过调用Writer1(semId, shm),Reader1(semId, shm),运行第一读写者程序(读优先),结果如下:

在这里插入图片描述

从上图我们可以看出程序大部分时间都在执行读进程,当有程序进行读时,不会有写进程执行写操作。进一步验证了读优先的策略的正确性。

4.2 “第二”读者-写者

通过调用Writer2(semId, shm),Reader2(semId, shm),运行第二读写者程序(写优先),结果如下:

在这里插入图片描述

从上图我们可以看出程序大部分时间都在执行写进程,当有写进程等待时,不会有读进程执行读操作。进一步验证了写优先的策略的正确性。

4.3 读者-写者公平策略

通过调用Writer3(semId, shm),Reader3(semId, shm),运行读者-写者公平策略程序(公平排队),结果如下:

在这里插入图片描述

从上图我们可以看出程序大部分执行读写进程的时间大致相同,无论读进程还是写进程到来,都需要排队,遵循先到先得的原则。进一步验证了读写-公平策略的正确性。

  • 13
    点赞
  • 105
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值