参考教材中的生产者消费者算法,创建5个进程,其中两个进程为生产者进程,3个进程为消费者进程。一个生产者进程试图不断地在一个缓冲中写入大写字母,另一个生产者进程试图不断地在缓冲中写入小写字母。3个消费者不断地从缓冲中读取一个字符并输出。为了使得程序的输出易于看到结果,仿照的实例程序,分别在生产者和消费者进程的合适的位置加入一些随机睡眠时间。 提示:为了使5个进程能够共享缓冲区,可以将这个缓冲区放置在一片共享内存,每一个进程将这片共享内存附接在自己的地址空间中。进程间的同步互斥请使用信号量。
时间: 2024-01-24 20:17:03 浏览: 138
以下是基于Linux系统下的C语言实现的代码:
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#include <time.h>
#define SHM_SIZE 1024
#define BUFFER_SIZE 10
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
struct shared_buffer {
char buffer[BUFFER_SIZE];
int head;
int tail;
};
void error_exit(char *message) {
fprintf(stderr, "%s\n", message);
exit(EXIT_FAILURE);
}
int create_shared_memory() {
key_t key = ftok(".", 'a');
int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
error_exit("Could not create shared memory");
}
return shmid;
}
int create_semaphore(int n) {
key_t key = ftok(".", 'b');
int semid = semget(key, n, IPC_CREAT | 0666);
if (semid == -1) {
error_exit("Could not create semaphore");
}
return semid;
}
void set_semaphore(int semid, int i, int value) {
union semun arg;
arg.val = value;
if (semctl(semid, i, SETVAL, arg) == -1) {
error_exit("Could not set semaphore value");
}
}
void sem_wait(int semid, int i) {
struct sembuf buf;
buf.sem_num = i;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
if (semop(semid, &buf, 1) == -1) {
error_exit("Could not execute sem_wait");
}
}
void sem_signal(int semid, int i) {
struct sembuf buf;
buf.sem_num = i;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
if (semop(semid, &buf, 1) == -1) {
error_exit("Could not execute sem_signal");
}
}
void destroy_shared_memory(int shmid, void *shmaddr) {
if (shmdt(shmaddr) == -1) {
error_exit("Could not detach shared memory");
}
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
error_exit("Could not remove shared memory");
}
}
void destroy_semaphore(int semid) {
if (semctl(semid, 0, IPC_RMID) == -1) {
error_exit("Could not remove semaphore");
}
}
void producer_upper_case(int shmid, int semid) {
struct shared_buffer *buffer = (struct shared_buffer *)shmat(shmid, NULL, 0);
if (buffer == (void *)-1) {
error_exit("Could not attach shared memory");
}
srand(time(NULL));
char upper_case_letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
while (1) {
char c = upper_case_letters[rand() % strlen(upper_case_letters)];
sem_wait(semid, 0);
sem_wait(semid, 2);
if ((buffer->tail + 1) % BUFFER_SIZE == buffer->head) {
printf("Buffer is full, producer_upper_case waits...\n");
sem_signal(semid, 2);
sleep(rand() % 5);
} else {
buffer->buffer[buffer->tail] = c;
buffer->tail = (buffer->tail + 1) % BUFFER_SIZE;
printf("producer_upper_case produced: %c\n", c);
sem_signal(semid, 1);
sem_signal(semid, 2);
sleep(rand() % 3);
}
}
destroy_shared_memory(shmid, buffer);
}
void producer_lower_case(int shmid, int semid) {
struct shared_buffer *buffer = (struct shared_buffer *)shmat(shmid, NULL, 0);
if (buffer == (void *)-1) {
error_exit("Could not attach shared memory");
}
srand(time(NULL));
char lower_case_letters[] = "abcdefghijklmnopqrstuvwxyz";
while (1) {
char c = lower_case_letters[rand() % strlen(lower_case_letters)];
sem_wait(semid, 0);
sem_wait(semid, 2);
if ((buffer->tail + 1) % BUFFER_SIZE == buffer->head) {
printf("Buffer is full, producer_lower_case waits...\n");
sem_signal(semid, 2);
sleep(rand() % 5);
} else {
buffer->buffer[buffer->tail] = c;
buffer->tail = (buffer->tail + 1) % BUFFER_SIZE;
printf("producer_lower_case produced: %c\n", c);
sem_signal(semid, 1);
sem_signal(semid, 2);
sleep(rand() % 3);
}
}
destroy_shared_memory(shmid, buffer);
}
void consumer(int shmid, int semid, char *name) {
struct shared_buffer *buffer = (struct shared_buffer *)shmat(shmid, NULL, 0);
if (buffer == (void *)-1) {
error_exit("Could not attach shared memory");
}
while (1) {
sem_wait(semid, 1);
sem_wait(semid, 2);
char c = buffer->buffer[buffer->head];
buffer->head = (buffer->head + 1) % BUFFER_SIZE;
printf("%s consumed: %c\n", name, c);
sem_signal(semid, 0);
sem_signal(semid, 2);
sleep(rand() % 3);
}
destroy_shared_memory(shmid, buffer);
}
int main() {
int shmid = create_shared_memory();
int semid = create_semaphore(3);
set_semaphore(semid, 0, BUFFER_SIZE);
set_semaphore(semid, 1, 0);
set_semaphore(semid, 2, 1);
pid_t pid_upper_case = fork();
if (pid_upper_case == -1) {
error_exit("Could not create producer_upper_case process");
} else if (pid_upper_case == 0) {
producer_upper_case(shmid, semid);
exit(EXIT_SUCCESS);
}
pid_t pid_lower_case = fork();
if (pid_lower_case == -1) {
error_exit("Could not create producer_lower_case process");
} else if (pid_lower_case == 0) {
producer_lower_case(shmid, semid);
exit(EXIT_SUCCESS);
}
pid_t pid_consumer1 = fork();
if (pid_consumer1 == -1) {
error_exit("Could not create consumer1 process");
} else if (pid_consumer1 == 0) {
consumer(shmid, semid, "consumer1");
exit(EXIT_SUCCESS);
}
pid_t pid_consumer2 = fork();
if (pid_consumer2 == -1) {
error_exit("Could not create consumer2 process");
} else if (pid_consumer2 == 0) {
consumer(shmid, semid, "consumer2");
exit(EXIT_SUCCESS);
}
pid_t pid_consumer3 = fork();
if (pid_consumer3 == -1) {
error_exit("Could not create consumer3 process");
} else if (pid_consumer3 == 0) {
consumer(shmid, semid, "consumer3");
exit(EXIT_SUCCESS);
}
wait(NULL);
destroy_semaphore(semid);
return 0;
}
```
在上面的代码中,我们使用了共享内存和信号量来实现进程之间的同步和互斥。`create_shared_memory` 函数创建并返回一片共享内存区域的标识符,`create_semaphore` 函数创建并返回一个包含 3 个信号量的信号量集合的标识符。`set_semaphore` 函数将一个信号量的值设置为指定的值,`sem_wait` 函数将一个信号量的值减 1,如果信号量的值小于 0,则阻塞进程,`sem_signal` 函数将一个信号量的值加 1。
`producer_upper_case` 函数是一个生产者进程,不断地在共享内存中的缓冲区中写入大写字母。在写入字母之前,它会先等待空闲的缓冲区位置和互斥锁。如果缓冲区已满,则它会等待一段随机时间再尝试写入。写入后,它会释放互斥锁并唤醒一个等待读取的消费者进程。`producer_lower_case` 函数是另一个生产者进程,不断地在共享内存中的缓冲区中写入小写字母,与 `producer_upper_case` 函数的实现类似。
`consumer` 函数是一个消费者进程,不断地从共享内存中的缓冲区中读取一个字符并输出。在读取前,它会先等待有可读取的字符和互斥锁。如果缓冲区为空,则它会等待一段随机时间再尝试读取。读取后,它会释放互斥锁并唤醒一个等待写入的生产者进程。
在 `main` 函数中,我们首先创建共享内存和信号量,并初始化信号量的值。然后,我们创建两个生产者进程和三个消费者进程。最后,我们等待所有子进程结束并销毁信号量。
阅读全文