用信号量机制解决生产者消费者问题 二、实验目的:通过研究Linux的进程机制和信号量实现生产者消费者问题的并发控制。
时间: 2024-01-24 08:17:30 浏览: 79
三、实验原理:
1. 生产者消费者问题
生产者消费者问题是指有一定数量的生产者和消费者共同操作在一个有限的缓冲区上,生产者向缓冲区中加入数据,消费者从缓冲区中取出数据,而生产者和消费者之间要使用同步机制来保证数据的正确性和一致性。
2. 信号量机制
信号量是一种用于多进程或多线程同步的基本工具,在Linux系统中实现信号量需要使用到信号量函数库。
信号量有两种类型:二元信号量和计数信号量。
二元信号量只有0和1两种状态,用于表示某个资源是否可用。
计数信号量可以有多个值,用于表示某个共享资源的可用数量。
Linux中实现信号量需要使用到三个函数:semget、semctl和semop。
- semget函数用于创建或获取一个信号量;
- semctl函数用于对信号量进行操作,如设置初始值、获取当前值等;
- semop函数用于对信号量进行P、V操作,即加锁和解锁。
四、实验步骤:
1. 创建一个共享内存区作为缓冲区,定义缓冲区大小和缓冲区头指针和尾指针。
2. 创建两个进程:生产者进程和消费者进程。
3. 生产者进程往缓冲区中写入数据,消费者进程从缓冲区中读取数据。
4. 使用信号量实现生产者和消费者的同步机制,保证生产者和消费者之间的数据不会出现错误。
5. 对于多个生产者和多个消费者的情况,需要使用多个信号量实现同步。
五、实验代码:
注:以下代码中省略了头文件和函数实现,仅给出主函数部分。
```c
#define BUF_SIZE 10
#define PRODUCER_NUM 2
#define CONSUMER_NUM 2
int shmid;
char* shmaddr;
int semid;
struct sembuf p = {0, -1, SEM_UNDO};
struct sembuf v = {0, 1, SEM_UNDO};
void producer(int id);
void consumer(int id);
int main()
{
// 创建共享内存
shmid = shmget(IPC_PRIVATE, BUF_SIZE, IPC_CREAT|0666);
if (shmid == -1)
{
perror("shmget error");
exit(EXIT_FAILURE);
}
// 映射共享内存
shmaddr = (char*)shmat(shmid, NULL, 0);
if (shmaddr == (char*)(-1))
{
perror("shmat error");
exit(EXIT_FAILURE);
}
// 初始化缓冲区头指针和尾指针
int* head = (int*)shmaddr;
int* tail = (int*)(shmaddr + sizeof(int));
*head = 0;
*tail = 0;
// 创建信号量
semid = semget(IPC_PRIVATE, 1, IPC_CREAT|0666);
if (semid == -1)
{
perror("semget error");
exit(EXIT_FAILURE);
}
// 初始化信号量
if (semctl(semid, 0, SETVAL, 1) == -1)
{
perror("semctl error");
exit(EXIT_FAILURE);
}
// 创建生产者进程
int i;
for (i = 1; i <= PRODUCER_NUM; i++)
{
pid_t pid = fork();
if (pid == -1)
{
perror("fork error");
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
producer(i);
exit(EXIT_SUCCESS);
}
}
// 创建消费者进程
for (i = 1; i <= CONSUMER_NUM; i++)
{
pid_t pid = fork();
if (pid == -1)
{
perror("fork error");
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
consumer(i);
exit(EXIT_SUCCESS);
}
}
// 等待子进程结束
for (i = 1; i <= PRODUCER_NUM + CONSUMER_NUM; i++)
{
wait(NULL);
}
// 删除共享内存
if (shmdt(shmaddr) == -1)
{
perror("shmdt error");
exit(EXIT_FAILURE);
}
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("shmctl error");
exit(EXIT_FAILURE);
}
// 删除信号量
if (semctl(semid, 0, IPC_RMID) == -1)
{
perror("semctl error");
exit(EXIT_FAILURE);
}
return 0;
}
void producer(int id)
{
int i;
for (i = 1; i <= 5; i++)
{
// 生产数据
char data = 'A' + id - 1;
printf("producer %d produce data %c\n", id, data);
// P操作
if (semop(semid, &p, 1) == -1)
{
perror("producer semop error");
exit(EXIT_FAILURE);
}
// 写入缓冲区
int* head = (int*)shmaddr;
int* tail = (int*)(shmaddr + sizeof(int));
char* buffer = shmaddr + 2 * sizeof(int);
while ((*head + 1) % BUF_SIZE == *tail)
{
// 缓冲区已满,等待
usleep(1000);
}
buffer[*head] = data;
*head = (*head + 1) % BUF_SIZE;
// V操作
if (semop(semid, &v, 1) == -1)
{
perror("producer semop error");
exit(EXIT_FAILURE);
}
// 等待一段时间
usleep(500000);
}
}
void consumer(int id)
{
int i;
for (i = 1; i <= 5; i++)
{
// P操作
if (semop(semid, &p, 1) == -1)
{
perror("consumer semop error");
exit(EXIT_FAILURE);
}
// 读取缓冲区
int* head = (int*)shmaddr;
int* tail = (int*)(shmaddr + sizeof(int));
char* buffer = shmaddr + 2 * sizeof(int);
while (*head == *tail)
{
// 缓冲区已空,等待
usleep(1000);
}
char data = buffer[*tail];
*tail = (*tail + 1) % BUF_SIZE;
// V操作
if (semop(semid, &v, 1) == -1)
{
perror("consumer semop error");
exit(EXIT_FAILURE);
}
// 消费数据
printf("consumer %d consume data %c\n", id, data);
// 等待一段时间
usleep(500000);
}
}
```
阅读全文