生产者-消费者-信号量-共享内存-循环队列

时间:2021-03-25 15:14:15
代码转自别处,稍有修改,主要是宏定义,我觉得不够直观的地方给改了,逻辑没变

用信号量来控制队列

#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define PRODUCER_NR	2
#define CONSUMER_NR	3
#define WORKS_P		6
#define WORKS_C		4

#define BUF_LENGTH	(sizeof(struct mybuffer))
#define LETTER_NUM	3
#define SHM_MODE	0600

#define SEM_ALL_KEY	1234
#define SEM_PRODUCER	0
#define SEM_CONSUMER	1

struct mybuffer
{
    char letter[LETTER_NUM];
    int head;
    int tail;
    int is_empty;
};

int get_random()
{
    struct timeval tms;

    gettimeofday(&tms, NULL);
    srand((unsigned)(getpid() + tms.tv_sec + tms.tv_usec));

    return rand() % 5;
}

char get_letter()
{
    struct timeval tms;

    gettimeofday(&tms, NULL);
    srand((unsigned)(getpid() + tms.tv_sec + tms.tv_usec));

    return (char)((char)(rand() % 26) + 'A');
}

void p_decrement(int sem_id, int sem_num)
{
    struct sembuf xx;
    xx.sem_num = sem_num;
    xx.sem_op = -1;
    xx.sem_flg = 0;
    semop(sem_id, &xx, 1);
}

void v_increment(int sem_id, int sem_num)
{
    struct sembuf xx;
    xx.sem_num = sem_num;
    xx.sem_op = 1;
    xx.sem_flg = 0;
    semop(sem_id, &xx, 1);
}

int main(int argc, char * argv[])
{
    int i, j;
    int shm_id, sem_id;
    int producer_nr = 0, consumer_nr = 0;
    struct mybuffer *shmptr;
    char lt;
    time_t now;
    pid_t pid_p, pid_c;

    sem_id = semget(SEM_ALL_KEY, 2, IPC_CREAT | 0660);
    if (sem_id >= 0) {
        printf("Main process starts. Semaphore created.\n");
    }
    semctl(sem_id, SEM_PRODUCER, SETVAL, LETTER_NUM);
    semctl(sem_id, SEM_CONSUMER, SETVAL, 0);
    
    if ((shm_id = shmget(IPC_PRIVATE, BUF_LENGTH, SHM_MODE)) < 0) {
        printf("Error on shmget.\n");
        exit(1);
    }
    if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1) {
        printf("Error on shmat.\n");
        exit(1);
    }
    shmptr->head = 0;
    shmptr->tail = 0;
    shmptr->is_empty = 1;
    
    /* create PRODUCER_NR child tasks as producers */
    while ((producer_nr++) < PRODUCER_NR) {
        if ((pid_p = fork()) < 0) {
            printf("Error on fork.\n");
            exit(1);
        }
        if (pid_p == 0) {
            if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1) {
                printf("Error on shmat.\n");
                exit(1);
            }
            for (i = 0; i < WORKS_P; i++) {
                p_decrement(sem_id, SEM_PRODUCER);
                sleep(get_random());
                shmptr->letter[shmptr->tail] = lt = get_letter();
                shmptr->tail = (shmptr->tail + 1) % LETTER_NUM;
                shmptr->is_empty = 0;
                now = time(NULL);
                printf("\tProducer %d puts '%c'.\t====>\tqueue:\t", producer_nr, lt);
                for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + LETTER_NUM)
				; !(shmptr->is_empty) && j >= shmptr->head
				; j--)
                {
                    printf("%c", shmptr->letter[j % LETTER_NUM]);
                }
		putchar('\n');
                fflush(stdout);
                v_increment(sem_id, SEM_CONSUMER);
            }
            shmdt(shmptr);
            exit(0);
        }
    }

    /* create CONSUMER_NR child tasks as consumers */
    while (consumer_nr++ < CONSUMER_NR) {
        if ((pid_c = fork()) < 0) {
            printf("Error on fork.\n");
            exit(1);
        }
        if (pid_c == 0) {
            if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1) {
                printf("Error on shmat.\n");
                exit(1);
            }
            for (i = 0; i < WORKS_C; i++) {
                p_decrement(sem_id, SEM_CONSUMER);
                sleep(get_random());
                lt = shmptr->letter[shmptr->head];
                shmptr->head = (shmptr->head + 1) % LETTER_NUM;
                shmptr->is_empty = (shmptr->head == shmptr->tail);
                now = time(NULL);
                printf("\tConsumer %d gets '%c'.\t====>\tqueue:\t", consumer_nr, lt);
                for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + LETTER_NUM)
				; !(shmptr->is_empty) && j >= shmptr->head
				; j--)
                {
                    printf("%c", shmptr->letter[j % LETTER_NUM]);
                }
		putchar('\n');
                fflush(stdout);
                v_increment(sem_id, SEM_PRODUCER);
            }
            shmdt(shmptr);
            exit(0);
        }
    }

    while(wait(0) != -1) {
    	static int child_nr = 1;
	printf("exit child %d\n", child_nr++);
    }

    shmdt(shmptr);
    shmctl(shm_id, IPC_RMID, 0);
    semctl(sem_id, IPC_RMID, 0);

    printf("Main process ends.\n");
    fflush(stdout);

    exit(0);
}

ref: http://www.cnblogs.com/xkfz007/articles/2566445.html