|
This page was last modified 2007-02-07 14:04:24 by Puchu.Net user Choco. (Show history)
The following code implements on Linux a classic producer/consumer program with IPC:
// prodcons.c
//
// code using signal.h, sem.h and shm.h
//
// created: 24sep2002 by Sean Yang (web.puchu@puchu.net)
// modified: 25sep2002
//
// remember:
// when screwing around with shared memory and semaphores, these commands
// are your friends: ipcs and ipcrm
//
// date description
// -------- ---------------------
// 25sep2002 added shm and signal stuff
// 24sep2002 initial version, demo binary and counting semaphores
#include <stdio.h>
#include <stdlib.h> // required for exit()
#include <unistd.h> // required for fork()
#include <signal.h>
#include <errno.h> // so I can check errno, but I don't :)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#define SEM_ACC_MODE 0600 // read is 4 write is 2 {0, user, group, other}
#define PRODUCE_MAX 38 // maximum items to produce for consuming
#define SHM_KEY 1977
// delay in seconds to make interesting
#define PROD_DELAY 1
#define CONS_DELAY 2
/* weird, semun doesn't seem to be defined? should be part of sem.h */
typedef union mysemun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
} semun;
//////////////////////////////////////////////////
// FUNCTION PROTOTYPES
//////////////////////////////////////////////////
void consumer(int child, int critical_region_id, int counting_sem_id,
char *shm_ptr);
void producer(int pid, int critical_region_id, int counting_sem_id,
char *shm_ptr);
void signal_term(int sig);
/* functions for semaphore operations */
void down(int semid);
void up(int semid);
//////////////////////////////////////////////////
// MAIN
//////////////////////////////////////////////////
int main(void)
{
// structure used to initialize the values of my semaphores
semun init_value;
// remember PID so that we can kill them later
pid_t child1, child2, child3, parent = getpid();
// gets semaphore id, must remove them when everything is consumed
int critical_region_id = semget(IPC_PRIVATE, 1, SEM_ACC_MODE);
int counting_sem_id = semget(IPC_PRIVATE, 1, SEM_ACC_MODE);
// allocate shared memory
int shared_mem_id = shmget(SHM_KEY, sizeof(char) * PRODUCE_MAX,
IPC_CREAT | SEM_ACC_MODE);
char *shm_ptr = (char *) shmat(shared_mem_id, (char *)NULL, 0);
// uninteresting variable
int prods;
// stuff for debugging, use these numbers to reclaim resources
printf("shm id is: %d\n", shared_mem_id);
printf("sem id is: %d\n", critical_region_id);
printf("sem id is: %d\n", counting_sem_id);
// install interrupt handler
signal(SIGTERM, signal_term);
// sets initial values for semaphore
init_value.val = 1;
if(semctl(critical_region_id, 0, SETVAL, init_value) == -1)
printf("critical_region_id SETVAL error\n");
init_value.val = 0;
if(semctl(counting_sem_id, 0, SETVAL, init_value) == -1)
printf("critical_region_id SETVAL error\n");
// attach to shared memory
if((child1 = fork()) < 0) printf("fork error\n");
if(child1) {
// children will be consumer
if((child2 = fork()) < 0) printf("fork error\n");
if(child2) {
if((child3 = fork()) < 0) printf("fork error\n");
if(child3) consumer(child3, critical_region_id, counting_sem_id, shm_ptr);
else consumer(child2, critical_region_id, counting_sem_id, shm_ptr);
}
else consumer(child1, critical_region_id, counting_sem_id, shm_ptr);
}
else {
// parent will be producer
producer(parent, critical_region_id, counting_sem_id, shm_ptr);
// find number of products available for consumption, should be zero when
// we clean up
prods = 1;
while (prods > 0) {
if((prods = semctl(counting_sem_id, 0, GETVAL)) == -1)
printf("counting_sem_id GETVAL error\n");
sleep(PROD_DELAY);
}
// detach shared memory
if ((shmdt(shm_ptr)) == -1) printf("detaching shared memory failed\n");
if(shmctl(shared_mem_id, 0, IPC_RMID) == -1)
printf("shared_mem_id shm RMID error\n");
printf("producer %d: shared memory detached\n", parent);
// removes semaphore when everything is consumed
if(semctl(critical_region_id, 0, IPC_RMID) == -1)
printf("critical_region_id sem RMID error\n");
if(semctl(counting_sem_id, 0, IPC_RMID) == -1)
printf("counting_sem_id sem RMID error\n");
printf("producer %d: semaphores removed\n", parent);
// signal child processes to exit
printf("killing processes...\n");
kill(child1, SIGTERM);
kill(child2, SIGTERM);
kill(child3, SIGTERM);
// give some time for children to die
sleep(2);
raise(SIGTERM);
}
// just in case
exit(EXIT_SUCCESS);
}
//////////////////////////////////////////////////
// FUNCTIONS
//////////////////////////////////////////////////
void consumer(int child, int critical_region_id, int counting_sem_id,
char *shm_ptr)
{
int prods;
printf("consumer %d says hello\n", child);
//allow time for introductions
sleep(8);
while(1) {
// find number of products available for consumption
if((prods = semctl(counting_sem_id, 0, GETVAL)) == -1)
printf("counting_sem_id GETVAL error\n");
while(prods < 1) {
sleep(CONS_DELAY);
if((prods = semctl(counting_sem_id, 0, GETVAL)) == -1)
printf("counting_sem_id GETVAL error\n");
}
// take semaphore, enter critical region
down(critical_region_id);
// we should reall check again and make sure that some one else did
// not consume my item, but Sean is lazy
printf("consumer %d: taking product %c, %d left\n",
child, shm_ptr[prods - 1], prods - 1);
// dec number of items to be consumed, just consumed 1
down(counting_sem_id);
up(critical_region_id);
sleep(CONS_DELAY);
}
}
void producer(int pid, int critical_region_id, int counting_sem_id,
char *shm_ptr)
{
int i, prods;
printf("PRODUCER %d says hi\n", pid);
//allow time for introductions
sleep(1);
for(i = 0; i < PRODUCE_MAX; i++) {
// take semaphore, enter critical region
down(critical_region_id);
if((prods = semctl(counting_sem_id, 0, GETVAL)) == -1)
printf("counting_sem_id GETVAL error\n");
shm_ptr[prods] = (char)((i % 26) + 'a');
printf("PRODUCER %d: giving product %c (#%d)\n", pid, shm_ptr[prods], i);
// inc number of items to be consumed
up(counting_sem_id);
up(critical_region_id);
sleep(PROD_DELAY);
}
}
void signal_term(int sig)
{
printf("process %d says bye bye\n", getpid());
exit(EXIT_SUCCESS);
}
void down(int semid)
{
struct sembuf op;
op.sem_num = op.sem_flg = 0;
op.sem_op = -1; // dec count by 1
if(semop(semid, &op, 1) == -1) printf("down semop error\n");
}
void up(int semid)
{
struct sembuf op;
op.sem_num = op.sem_flg = 0;
op.sem_op = 1; // inc count by 1
if(semop(semid, &op, 1) == -1) printf("down semop error\n");
}
|
|
|||