设计思路
- 共享缓冲区:使用共享内存来实现共享缓冲区,以让多个进程可以访问。
- 信号量:
- 创建三个信号量:一个用于表示共享缓冲区中的空闲槽位(
empty
),初始值为缓冲区大小;一个用于表示共享缓冲区中的已占用槽位(full
),初始值为0;另一个用于实现互斥访问共享缓冲区(mutex
),初始值为1。
- 进程A(生产者):先获取
empty
信号量(表示有空闲槽位才能生产),然后获取mutex
信号量(互斥访问共享缓冲区),生产数据放入缓冲区,再释放mutex
信号量,最后释放full
信号量(表示缓冲区有数据了)。
- 进程B和进程C(消费者):先获取
full
信号量(表示有数据才能消费),然后获取mutex
信号量(互斥访问共享缓冲区),从缓冲区消费数据,再释放mutex
信号量,最后释放empty
信号量(表示缓冲区有空闲槽位了)。
- 避免死锁:按照固定的顺序获取信号量(先
empty
/full
,再mutex
),并且在获取信号量失败时进行合理处理(例如等待或重试)。
核心代码片段
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <unistd.h>
#define BUFFER_SIZE 5
// 定义信号量操作函数
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
void P(int semid, int semnum) {
struct sembuf sops = {semnum, -1, 0};
semop(semid, &sops, 1);
}
void V(int semid, int semnum) {
struct sembuf sops = {semnum, 1, 0};
semop(semid, &sops, 1);
}
int main() {
key_t key;
int semid, shmid;
int *shared_buffer;
// 创建共享内存
key = ftok(".", 'a');
shmid = shmget(key, sizeof(int) * BUFFER_SIZE, IPC_CREAT | 0666);
shared_buffer = (int *)shmat(shmid, NULL, 0);
// 创建信号量
key = ftok(".", 'b');
semid = semget(key, 3, IPC_CREAT | 0666);
union semun arg;
// 初始化信号量
arg.val = BUFFER_SIZE;
semctl(semid, 0, SETVAL, arg); // empty
arg.val = 0;
semctl(semid, 1, SETVAL, arg); // full
arg.val = 1;
semctl(semid, 2, SETVAL, arg); // mutex
// 创建进程A
if (fork() == 0) {
for (int i = 0; i < 10; i++) {
P(semid, 0); // 获取empty信号量
P(semid, 2); // 获取mutex信号量
shared_buffer[i % BUFFER_SIZE] = i;
printf("Producer A produced %d\n", i);
V(semid, 2); // 释放mutex信号量
V(semid, 1); // 释放full信号量
}
exit(0);
}
// 创建进程B
if (fork() == 0) {
for (int i = 0; i < 5; i++) {
P(semid, 1); // 获取full信号量
P(semid, 2); // 获取mutex信号量
int data = shared_buffer[i % BUFFER_SIZE];
printf("Consumer B consumed %d\n", data);
V(semid, 2); // 释放mutex信号量
V(semid, 0); // 释放empty信号量
}
exit(0);
}
// 创建进程C
if (fork() == 0) {
for (int i = 0; i < 5; i++) {
P(semid, 1); // 获取full信号量
P(semid, 2); // 获取mutex信号量
int data = shared_buffer[(i + 5) % BUFFER_SIZE];
printf("Consumer C consumed %d\n", data);
V(semid, 2); // 释放mutex信号量
V(semid, 0); // 释放empty信号量
}
exit(0);
}
// 等待子进程结束
wait(NULL);
wait(NULL);
wait(NULL);
// 清理共享内存和信号量
shmdt(shared_buffer);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID, 0);
return 0;
}