信号量生产者消费

    技术2022-05-20  55

     

    网上流传的有个版本会有下面所述的问题,这个是自己的作业。

     

    实验二 Linux 进程同步

    1. 关键问题

    1) 实现子进程在父进程写数据之后才开始读取数据

    2) 实现在所有子进程读取数据之后父进程才开始写数据

    3) 在读取的时候不互斥缓冲区,实现数据的并发读取

    2. 设计思路

    本程序总体设计思想为,一个父进程(生产者)写数据,任意数量个(超过 3 个)子进程(消费者)可以并发地读取数据。

    对于第一个问题,我们使用名为 semread 的信号量。它的初始值为 0 ,子进程(消费者)在开始运行时执行 P(1) ,把信号量的值减 1 ,所以子进程会进入等待。待父进程(生产者)写入数据后进行 V(3) 操作,把信号量的值加上 3 。然后 3 个子进程开始运行。

    对于第二个问题,我们使用名为 semwrite 的信号量。在父进程对 semread 操作唤醒所有等待的子进程之后。父进程对 semwrite 执行 P(3) 操作, semwrite 的初始值为 0 ,所以父进程会进入休眠状态。待三个子进程读取数据之后。每个子进程都对 semwrite 执行 V(1) 操作。然后父进程就会被唤醒。

    上面,似乎已经解决了问题。但是,其实没有。如果某个子进程处理速度很快的话。那么它可能会两次或更多次成功对 semread 执行 P 1 )操作,然后对 semwrite 执行 V(1) 操作,那么,另外一个子进程在没有读取数据的情况下,父进程被唤醒。因此我们引定第三个信号量的定义,一个初值为 0 名字 semchild 的信号量。在子进程对 semwrite 进行 V(1) 操作后,还要执行 semchild P 1 )操作,由于 semchild 的初始值为 0 。所以子进程会进入休眠状态。不会再有机会有多次成功对 semread 的可能。由于是先进行对 semwrite 进行 V(1) 。所以,父进程会从对 semwrite P(3) 操作中唤醒。这时三个子进程休眠在对 semchlid P(1) 操作中。父进程对 semchild 进行 V(3) 操作,所有子进程被唤醒,子进程又进入了对 semread 的等中。

    3. 实现的关键代码

    #include <stdio.h>

    #include <sys/mman.h>

    #include <unistd.h>

    #include <time.h>

    #include <stdlib.h>

    #include <sys/sem.h>

     

    int semwrite; /*can write*/

    int semread; /*can read*/

    int semchild; /*for child */

    int *data; /*for share data*/

    struct sembuf p1, p3; /*pn n is sub num*/

    struct sembuf v1, v3; /*vn n is add num*/

     

     

    int customer_read()

    {

    while(1)

    {

    semop(semread, &p1, 1);

    if (*data > 0) {

    printf("child process %d:parent data %d/n",

    getpid(), *data);

    semop(semwrite, &v1, 1);

    semop(semchild, &p1, 1);

    } else {

    printf("chlid process %d:exit signal recive/n",

    getpid());

    semop(semwrite, &v1, 1);

    exit(0);

    }

    }

    }

     

     

    int fork_child()

    {

    pid_t ret = fork();

     

    if (ret == 0) {

    customer_read();

    } else if (ret > 0) {

    return ret;

    } else {

    perror("fork error");

    exit(ret);

    }

    }

     

    int main()

    {

    int semarg;

     

    data = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,

    MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    semwrite = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);

    semread = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);

    semchild = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);

     

    if(semwrite < 0 && semread < 0) {

    perror("semget error");

    return 0;

    }

     

    semarg = 0;

    if (semctl(semwrite, 0, SETVAL, semarg) == -1) {

    perror("semctl error");

    return 0;

    }

    if (semctl(semread, 0, SETVAL, semarg) == -1) {

    perror("semctl error");

    return 0;

    }

     

    v1.sem_num = 0;

    v1.sem_op = 1;

    v1.sem_flg = 0; /*it's form man semop: If sem_op is a positive integer and the calling process has alter permission, the value of sem_op shall be added to semval and, if (sem_flg &SEM_UNDO) is non-zero, the value of sem_op shall be subtracted from the calling process' semadj value for the specified semaphore*/

    v3.sem_num = 0;

    v3.sem_op = 3;

    v3.sem_flg = 0;

     

    p1.sem_num = 0;

    p1.sem_op = -1;

    p1.sem_flg = 0;

    p3.sem_num = 0;

    p3.sem_op = -3;

    p3.sem_flg = 0;

     

    fork_child();

    fork_child();

    fork_child();

     

    int i;

     

    for (i = 0; i < 5; i++)

    {

    srand(time(NULL));

    *data = rand();

    semop(semread, &v3, 1);

    semop(semwrite, &p3, 1);

    semop(semchild, &v3, 1);

    sleep(1);

    }

     

    *data = -1;

    semop(semread, &v3, 1);

    wait();

    wait();

    wait();

    printf("all child process exit/n");

    }

    4. 程序运行结果

     

    5. 总结及进一步改善建议

    通过这次实验,深刻体会到了并发和同步编程的困难。开始想只使用两个信号来实现这个实验。但是到最后,还是用了三个。但是这个程序是完美的。不会受到进程调度的影响。改善的就是,使用mmap 映射内存后要调用munmap 来取消映射。


    最新回复(0)