信号量生产者消费

本文介绍了一个关于 Linux 下进程间同步的实验案例,详细解释了如何使用信号量解决进程同步问题,包括确保子进程在父进程写入数据后读取数据的方法,以及如何避免子进程之间的数据竞争。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

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

 

实验二 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 来取消映射。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值