生产者消费者信号量值不正确
Producer consumer semaphore value is not correct
我正在尝试使用进程和 System V IPC 在 C 中实现生产者-消费者问题,但我坚持做一件事。这是我用来学习和测试信号量如何工作的代码的早期版本(没有实现队列操作,甚至没有执行循环中的生产者和消费者):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <time.h>
#include "sem.h"
#include "shm.h"
#define BUFFER_SIZE 9
int full;
int empty;
int mutex;
struct buff {
int queue[BUFFER_SIZE];
} *buffer;
void producer();
void consumer();
int main() {
int parent_pid, pid, i;
parent_pid = getpid();
int shmid = allocate_shared_memory(sizeof(*buffer));
buffer = (struct buff *) attach_shared_memory(shmid);
for (i = 0; i < BUFFER_SIZE; i++) {
buffer->queue[i] = 0;
}
full = sem_allocate();
empty = sem_allocate();
mutex = sem_allocate();
printf("Full %d\n", full);
printf("Empty %d\n", empty);
printf("Mutex %d\n", mutex);
sem_init(full, 0);
sem_init(empty, BUFFER_SIZE);
sem_init(mutex, 1);
printf("Full value %d\n", sem_get_val(full));
printf("Empty value %d\n", sem_get_val(empty));
printf("Mutex value %d\n", sem_get_val(mutex));
srand(time(0));
pid = fork();
if (!pid) {
printf("Producer here: %d\n", getpid());
producer();
printf("Full value after prod() %d\n", sem_get_val(full));
return 0;
} else printf("Created new producent: %d\n", pid);
sleep(1);
pid = fork();
if (!pid) {
printf("Consumer here: %d\n", getpid());
printf("Full value before cons() %d\n", sem_get_val(full)); //here I always get 0
consumer();
return 0;
} else printf("Created new consumer: %d\n", pid);
while (1)
{
int status;
pid_t done = wait(&status);
if (done == -1)
{
if (errno == ECHILD) break; // no more child processes
}
else
{
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
exit(1);
}
}
}
if (getpid() == parent_pid) {
sem_deallocate(full);
sem_deallocate(empty);
sem_deallocate(mutex);
}
}
void producer() {
sem_wait(empty);
sem_wait(mutex);
printf("Producer is producing!\n");
buffer->queue[0]=0;
sem_post(mutex);
sem_post(full);
}
void consumer() {
sem_wait(full);
sem_wait(mutex);
printf("Consumer is consuming!\n");
sem_post(mutex);
sem_post(empty);
}
int sem_allocate() {
return semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
}
void sem_deallocate(int semid) {
if (semctl(semid, 0, IPC_RMID, NULL) == -1)
{
perror("Error releasing semaphore!\n");
exit(EXIT_FAILURE);
}
}
int sem_init(int semid, int value) {
union semun arg;
arg.val = value;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror("semctl");
return -1;
}else return 1;
}
int sem_wait(int semid) {
printf("Someone is waiting %d\n", semid);
struct sembuf sem = { 0, -1, SEM_UNDO };
return semop(semid, &sem, 1);
}
int sem_post(int semid) {
printf("Someone is posting %d\n", semid);
struct sembuf sem = { 0, 1, SEM_UNDO };
return semop(semid, &sem, 1);
}
int sem_get_val(int semid) {
return semctl(semid, 0, GETVAL, 0);
}
int allocate_shared_memory(int size) {
return shmget(IPC_PRIVATE, size, IPC_CREAT | SHM_W | SHM_R);
}
void deallocate_shared_memory(const void* addr, int shmid) {
shmctl(shmid, IPC_RMID, 0);
}
void* attach_shared_memory(int shmid) {
return shmat(shmid, NULL, 0);
}
sem.h:
#include <sys/types.h>
#include <errno.h>
union semun {
int val;
struct semid_ds *buf;
ushort* array;
};
int sem_post(int);
int sem_wait(int);
int sem_allocate();
void sem_deallocate(int);
int sem_init(int, int);
int sem_get_val(int);
shm.h:
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
int allocate_shared_memory(int size);
void deallocate_shared_memory(const void* addr, int shmid);
void* attach_shared_memory(int shmid);
为什么full semaphore的consumer函数执行前的值为0?即使在生产者完成他的工作之后,该值也是 1...
我是这类话题的新手,所以可能对这种情况有明显的解释,但我不知道我能做什么,希望你能帮助我。
您将 "full" 信号量初始化为零。您的 "child" 生产者在退出之前调用您的 sem_post()
函数,该函数使用 SEM_UNDO
参数调用 semop()
。
int sem_post(int semid) {
printf("Someone is posting %d\n", semid);
struct sembuf sem = { 0, 1, SEM_UNDO };
return semop(semid, &sem, 1);
}
semop 的 Ubuntu Linux 手册页说明了以下关于 SEM_UNDO 的内容:
... If an operation specifies SEM_UNDO, it will be automatically
undone when the process terminates.
这意味着,"producer" 在退出之前递增 "full",然后在退出系统后 "undoes" 递增(即递减 "full")将其设置回零。
因此,出于 "full" 信号量的目的,您不应指定 SEM_UNDO。
我正在尝试使用进程和 System V IPC 在 C 中实现生产者-消费者问题,但我坚持做一件事。这是我用来学习和测试信号量如何工作的代码的早期版本(没有实现队列操作,甚至没有执行循环中的生产者和消费者):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <time.h>
#include "sem.h"
#include "shm.h"
#define BUFFER_SIZE 9
int full;
int empty;
int mutex;
struct buff {
int queue[BUFFER_SIZE];
} *buffer;
void producer();
void consumer();
int main() {
int parent_pid, pid, i;
parent_pid = getpid();
int shmid = allocate_shared_memory(sizeof(*buffer));
buffer = (struct buff *) attach_shared_memory(shmid);
for (i = 0; i < BUFFER_SIZE; i++) {
buffer->queue[i] = 0;
}
full = sem_allocate();
empty = sem_allocate();
mutex = sem_allocate();
printf("Full %d\n", full);
printf("Empty %d\n", empty);
printf("Mutex %d\n", mutex);
sem_init(full, 0);
sem_init(empty, BUFFER_SIZE);
sem_init(mutex, 1);
printf("Full value %d\n", sem_get_val(full));
printf("Empty value %d\n", sem_get_val(empty));
printf("Mutex value %d\n", sem_get_val(mutex));
srand(time(0));
pid = fork();
if (!pid) {
printf("Producer here: %d\n", getpid());
producer();
printf("Full value after prod() %d\n", sem_get_val(full));
return 0;
} else printf("Created new producent: %d\n", pid);
sleep(1);
pid = fork();
if (!pid) {
printf("Consumer here: %d\n", getpid());
printf("Full value before cons() %d\n", sem_get_val(full)); //here I always get 0
consumer();
return 0;
} else printf("Created new consumer: %d\n", pid);
while (1)
{
int status;
pid_t done = wait(&status);
if (done == -1)
{
if (errno == ECHILD) break; // no more child processes
}
else
{
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
exit(1);
}
}
}
if (getpid() == parent_pid) {
sem_deallocate(full);
sem_deallocate(empty);
sem_deallocate(mutex);
}
}
void producer() {
sem_wait(empty);
sem_wait(mutex);
printf("Producer is producing!\n");
buffer->queue[0]=0;
sem_post(mutex);
sem_post(full);
}
void consumer() {
sem_wait(full);
sem_wait(mutex);
printf("Consumer is consuming!\n");
sem_post(mutex);
sem_post(empty);
}
int sem_allocate() {
return semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
}
void sem_deallocate(int semid) {
if (semctl(semid, 0, IPC_RMID, NULL) == -1)
{
perror("Error releasing semaphore!\n");
exit(EXIT_FAILURE);
}
}
int sem_init(int semid, int value) {
union semun arg;
arg.val = value;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror("semctl");
return -1;
}else return 1;
}
int sem_wait(int semid) {
printf("Someone is waiting %d\n", semid);
struct sembuf sem = { 0, -1, SEM_UNDO };
return semop(semid, &sem, 1);
}
int sem_post(int semid) {
printf("Someone is posting %d\n", semid);
struct sembuf sem = { 0, 1, SEM_UNDO };
return semop(semid, &sem, 1);
}
int sem_get_val(int semid) {
return semctl(semid, 0, GETVAL, 0);
}
int allocate_shared_memory(int size) {
return shmget(IPC_PRIVATE, size, IPC_CREAT | SHM_W | SHM_R);
}
void deallocate_shared_memory(const void* addr, int shmid) {
shmctl(shmid, IPC_RMID, 0);
}
void* attach_shared_memory(int shmid) {
return shmat(shmid, NULL, 0);
}
sem.h:
#include <sys/types.h>
#include <errno.h>
union semun {
int val;
struct semid_ds *buf;
ushort* array;
};
int sem_post(int);
int sem_wait(int);
int sem_allocate();
void sem_deallocate(int);
int sem_init(int, int);
int sem_get_val(int);
shm.h:
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
int allocate_shared_memory(int size);
void deallocate_shared_memory(const void* addr, int shmid);
void* attach_shared_memory(int shmid);
为什么full semaphore的consumer函数执行前的值为0?即使在生产者完成他的工作之后,该值也是 1...
我是这类话题的新手,所以可能对这种情况有明显的解释,但我不知道我能做什么,希望你能帮助我。
您将 "full" 信号量初始化为零。您的 "child" 生产者在退出之前调用您的 sem_post()
函数,该函数使用 SEM_UNDO
参数调用 semop()
。
int sem_post(int semid) {
printf("Someone is posting %d\n", semid);
struct sembuf sem = { 0, 1, SEM_UNDO };
return semop(semid, &sem, 1);
}
semop 的 Ubuntu Linux 手册页说明了以下关于 SEM_UNDO 的内容:
... If an operation specifies SEM_UNDO, it will be automatically undone when the process terminates.
这意味着,"producer" 在退出之前递增 "full",然后在退出系统后 "undoes" 递增(即递减 "full")将其设置回零。
因此,出于 "full" 信号量的目的,您不应指定 SEM_UNDO。