第三十三章 System V共享内存与信号量综合

用信号量解决生产者、消费者问题

实现shmfifo

ip.h

#ifndef _IPC_H
#define _IPC_H

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)


int sem_create(key_t key);
int sem_del(int semid);
int sem_open(key_t key);
int sem_setval(int semid, int val);
int sem_getval(int semid, int val);
int sem_p(int semid);
int sem_v(int semid);

#endif // !_IPC_H

ip.c

#include "ipc.h"

int sem_create(key_t key)
{
    int semid;
    semid = semget(key, 1, IPC_CREAT|IPC_EXCL| 0666);
    if(semid == -1)
        ERR_EXIT("semget");
    
    return semid;
}

int sem_open(key_t key)
{
    int semid;
    semid = semget(key, 0, 0);
    if(semid == -1)
        ERR_EXIT("semget");

    return semid;
}

int sem_setval(int semid, int val)
{
    union semun su;
    su.val = val;
    int ret;
    ret = semctl(semid, 0, SETVAL, su);
    if(ret == -1)
        ERR_EXIT("semctl");

    return 0;
}

int sem_del(int semid)
{
    int ret;
    ret = semctl(semid, 0, IPC_RMID, 0);
    if(ret == -1)
        ERR_EXIT("semctl");

    return 0;
}

int sem_getval(int semid, int val)
{
    int ret;
    ret = semctl(semid, 0, GETVAL, 0);
    if(ret == -1)
        ERR_EXIT("semctl getval");

    return ret;
}

int sem_p(int semid)
{
    struct sembuf sops = {0, -1, 0};
    int ret;
    ret = semop(semid, &sops, 1);
    if(ret == -1)
        ERR_EXIT("semop");

    return 0;
}

int sem_v(int semid)
{
    struct sembuf sops = {0, 1, 0};
    int ret;
    ret = semop(semid, &sops, 1);
    if(ret == -1)
        ERR_EXIT("semop");

    return 0;
}

shmfifo.h

#ifndef SHM_FIFO_H
#define SHM_FIFO_H

#include "ipc.h"

typedef struct shmfifo shmfifo_t;
typedef struct shmhead shmhead_t;

struct shmhead
{
    unsigned int blksize;       //块大小
    unsigned int blocks;        //总块数
    unsigned int rd_index;      //读索引
    unsigned int wr_index;      //写索引
};


struct shmfifo
{
    shmhead_t *p_shm;       //共享内存头部指针
    char *p_payload;        //有效负载的起始地址

    int shmid;              //共享内存ID
    int sem_mutex;          //用来互斥用的信号量
    int sem_full;           //用来控制共享内存是否满的信号量
    int sem_empty;          //用来控制共享内存是否空的信号量
};

shmfifo_t* shmfifo_init(int key, int blksize, int blocks);
void shmfifo_put(shmfifo_t* fifo, const void* buf);
void shmfifo_get(shmfifo_t* fifo, void* buf);
void shmfifo_destory(shmfifo_t *fifo);


#endif // !SHM_FIFO_H

shmfifo.c

#include "shmfifo.h"
#include <assert.h>

#include <sys/ipc.h>
#include <sys/shm.h>

shmfifo_t* shmfifo_init(int key, int blksize, int blocks)
{
    shmfifo_t *fifo = (shmfifo_t*)malloc(sizeof(shmfifo_t));
    assert(fifo != NULL);
    memset(fifo, 0, sizeof(shmfifo_t));

    int shmid;
    
    //假设共享内存已存在,直接打开
    shmid = shmget(key, 0, 0);
    int size = sizeof(shmhead_t) + blksize*blocks;
    
    
    if(shmid == -1)
    {
        //打开失败,表示之前没有创建共享内存
        fifo->shmid = shmget(key, size, IPC_CREAT|0666);
        if(fifo->shmid == -1)
            ERR_EXIT("shmget");

        //把共享内存区对象映射到调用进程的地址空间
        fifo->p_shm = (shmhead_t*)shmat(fifo->shmid, NULL, 0);
        if(fifo->p_shm == (shmhead_t*)-1)
            ERR_EXIT("shmat");

        //初始化fifo这个结构
        fifo->p_payload = (char *)(fifo->p_shm + 1);
        fifo->p_shm->blksize = blksize;
        fifo->p_shm->blocks = blocks;
        fifo->p_shm->rd_index = 0;
        fifo->p_shm->wr_index = 0;

        //创建信号量
        fifo->sem_mutex = sem_create(key);
        fifo->sem_full = sem_create(key+1);
        fifo->sem_empty = sem_create(key+2);
        
        //设置信号量初始值
        sem_setval(fifo->sem_mutex, 1);
        sem_setval(fifo->sem_full, blocks);
        sem_setval(fifo->sem_empty, 0);

    }
    else
    {
        //打开成功,共享内存和信号量已经存在
        //共享内存和信号量使用的同一个key;共享内存之间或信号量之间不能用一个key

        fifo->shmid = shmid;
        fifo->p_shm = shmat(fifo->shmid, NULL, 0);
        if(fifo->p_shm == (shmhead_t*)-1)
            ERR_EXIT("shmat");
        
        fifo->p_payload = (char *)fifo->p_shm+1;
        
        fifo->sem_mutex = sem_open(key);
        fifo->sem_full = sem_open(key+1);
        fifo->sem_empty = sem_open(key+2);
    }
    
    return fifo;
}


void shmfifo_put(shmfifo_t* fifo, const void* buf)
{
    //对信号量sem_full进行P操作;(sem_full = blocks)
    //sem_full--
    //若sem_full减1后仍大于或等于0,则进程继续执行
    //若sem_full减1后小于0,则该进程被阻塞后放入等待该信号量的等待队列中,然后转进程调度
    sem_p(fifo->sem_full);
    
    //  >=0
    //进程对信号量sem_mutex进行P操作,其它写进程将阻塞
    sem_p(fifo->sem_mutex);

    //将buf拷贝到共享内存中
    memcpy(fifo->p_payload + fifo->p_shm->blksize*fifo->p_shm->wr_index, buf, fifo->p_shm->blksize);
    //更新写的下标
    fifo->p_shm->wr_index = (fifo->p_shm->wr_index+1) % fifo->p_shm->blksize;

    //进程对信号量sem_mutex进行V操作,其他写进程可以执行
    sem_v(fifo->sem_mutex);

    //对共享内存进行V操作;
    //sem_empty ++
    //若加1后结果大于0,则进程继续执行;
    //若相加后结果小于或等于0,则从该信号的等待队列中释放一个等待进程,然后再返回原进程继续执行或转进程调度。
    sem_v(fifo->sem_empty);
}

void shmfifo_get(shmfifo_t* fifo, void* buf)
{
    //对信号量sem_empty进行P操作;(sem_full = 0)
    //sem_empty--
    //若sem_empty减1后仍大于或等于0,则进程继续执行
    //若sem_empty减1后小于0,则该进程被阻塞后放入等待该信号量的等待队列中,然后转进程调度
    sem_p(fifo->sem_empty);

    //  >=0
    //进程对信号量sem_mutex进行P操作,其它读进程将阻塞
    sem_p(fifo->sem_mutex);

    //将共享内存拷贝到buf中
    memcpy( buf, fifo->p_payload + fifo->p_shm->blksize*fifo->p_shm->rd_index, fifo->p_shm->blksize);
    //更新读的下标
    fifo->p_shm->rd_index = (fifo->p_shm->rd_index+1) % fifo->p_shm->blksize;

    //进程对信号量sem_mutex进行V操作,其他写进程可以执行
    sem_v(fifo->sem_mutex);

    //对共享内存进行V操作;
    //sem_full ++
    //若加1后结果大于0,则进程继续执行;
    //若相加后结果小于或等于0,则从该信号的等待队列中释放一个等待进程,然后再返回原进程继续执行或转进程调度。
    sem_v(fifo->sem_full);
}

void shmfifo_destory(shmfifo_t *fifo)
{
    sem_del(fifo->sem_mutex);
    sem_del(fifo->sem_full);
    sem_del(fifo->sem_empty);

    shmdt(fifo->p_shm);
    shmctl(fifo->shmid, IPC_RMID, NULL);

    free(fifo);
}

shmfifo_send.c

#include "shmfifo.h"

typedef struct stu
{
    int name[32];
    int age;
}STU;

int main(void)
{
    shmfifo_t *fifo = shmfifo_init(1234, sizeof(STU), 3);

    STU s;
    memset(&s, 9, sizeof(STU));
    s.name[0] = 'A';

    int i;
    for(i=0; i<5; ++i)
    {
        s.age = 20 + i;
        shmfifo_put(fifo,&s);
        s.name[0] = s.name[0] + 1;

        printf("send ok\n");
    }
    return 0;
}

shmfifo_recv.c

#include "shmfifo.h"


typedef struct stu
{
    int name[32];
    int age;
}STU;

int main(void)
{
    shmfifo_t *fifo = shmfifo_init(1234, sizeof(STU), 3);

    STU s;
    memset(&s, 9, sizeof(STU));

    int i;
    for(i=0; i<5; ++i)
    {
        s.age = 20 + i;
        shmfifo_get(fifo,&s);
        printf("name = %s age = %d\n",s.name, s.age);
    }
    return 0;
}

shmfifo_free

#include "shmfifo.h"

typedef struct stu
{
    int name[32];
    int age;
}STU;

int main(void)
{
    shmfifo_t *fifo = shmfifo_init(1234, sizeof(STU), 3);
    shmfifo_destory(fifo);

    return 0;
}

Makefile

.PHONY:clean all

CC=gcc
CFLAGS=-Wall -g 

BIN = shmfifo_recv shmfifo_send shmfifo_free
OBJS1 = shmfifo_recv.o shmfifo.o ipc.o
OBJS2 = shmfifo_send.o shmfifo.o ipc.o
OBJS3 = shmfifo_free.o shmfifo.o ipc.o
all:$(BIN)

%.o:%.c
	$(CC) $(CFLAGS) -c $< -o $@

shmfifo_recv: ${OBJS1}
	$(CC) $(CFLAGS) $^ -o $@

shmfifo_send: ${OBJS2}
	$(CC) $(CFLAGS) $^ -o $@

shmfifo_free: ${OBJS3}
	$(CC) $(CFLAGS) $^ -o $@

clean:
	rm -rf *.o $(BIN)
posted @ 2019-09-26 19:21  sfdevs  阅读(198)  评论(0编辑  收藏  举报