Linux IPC实践(13) --System V IPC综合实践

来源:互联网 发布:英菲克看电视软件下载 编辑:程序博客网 时间:2024/05/16 15:01

实践:实现一个先进先出的共享内存shmfifo

 

   使用消息队列即可实现消息的先进先出(FIFO), 但是使用共享内存实现消息的先进先出则更加快速;

   我们首先完成C语言版本的shmfifo(基于过程调用), 然后在此基础上实现C++版本的ShmFifo, 将1块共享内存与3个信号量(1个mutext信号量, 1个full信号量, 1个empty信号量)封装成一个类ShmFifo, 然后编写各自的测试代码;

 

shmfifo说明:

   将申请到的共享内存作为一块缓冲区, 将该内存的首部(p_shm到p_payload的内容)格式化为如上图所示的形式;

   读/写进程不断的按照现金先出的原则从其中读出/写入数据, 则读/写进程就可以看成生产者/消费者了, 因此,使用信号量sem_mutex(初值为1)来互斥访问共享内存, 使用sem_full(初值为共享缓冲区块数), sem_empty(初值为0)来同步两个进程.

 

C版本:

//结构体类型定义typedef struct shmhead shmhead_t;typedef struct shmfifo shmfifo_t;//共享内存首部定义struct shmhead{    unsigned int blksize;   //块大小    unsigned int blocks;    //总块数    unsigned int rd_index;  //读索引块    unsigned int wr_index;  //写索引块};//整个shmfifo句柄struct shmfifo{    shmhead_t *p_shm;   //共享内存头部指针    char *p_payload;    //有效负载其实地址    int shmid;      //共享内存ID    int sem_mutex;  //互斥信号量    int sem_full;   //满信号量    int sem_empty;  //空信号量};
/**shmfifo初始化既包含了共享内存的初始化, 也包含了三个信号量的初始化;小技巧: 对一个IPC对象首先尝试打开, 如果打开失败, 则表示该IPC对象尚未创建, 则需要创建之, 而如果打开成功的话, 则进行其他操作**/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 = shmget(key, 0, 0);    // 如果打开失败, 则表示该共享内存尚未创建, 则创建    if (shmid == -1)    {        /** 设置共享内存 **/        int size = blksize*blocks + sizeof(shmhead_t);        //创建共享内存        fifo->shmid = shmget(key, size, IPC_CREAT|0666);        if (fifo->shmid == -1)            err_exit("shmget error");        //创建共享内存成功, 则需要将其连接到进程的地址空间        //void *shmat(int shmid, const void *shmaddr, int shmflg);        fifo->p_shm = (shmhead_t *)shmat(fifo->shmid, NULL, 0);        if (fifo->p_shm == (void *) -1)            err_exit("shmat error");        //将共享内存的首部初始化为struct shmhead        fifo->p_shm->blksize = blksize;        fifo->p_shm->blocks = blocks;        fifo->p_shm->rd_index = 0;        fifo->p_shm->wr_index = 0;        fifo->p_payload = (char *)(fifo->p_shm+1);        /** 设置三个信号量 **/        fifo->sem_mutex = sem_create(key);        sem_setval(fifo->sem_mutex, 1);        fifo->sem_full = sem_create(key+1);        sem_setval(fifo->sem_full, 10);        fifo->sem_empty = sem_create(key+2);        sem_setval(fifo->sem_empty, 0);    }    else    {        fifo->shmid = shmid;        //共享内存已经存在, 并且打开成功, 则需要将其连接到进程的地址空间        fifo->p_shm = (shmhead_t *)shmat(fifo->shmid, NULL, 0);        if (fifo->p_shm == (void *) -1)            err_exit("shmat error");        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;}
/**shmfifo的销毁既要销毁共享内存, 也要销毁三个信号量, 还需要将malloc出来的shmfifo_t结构体释放掉**/void shmfifo_destroy(shmfifo_t *fifo){    //释放三个信号量    sem_delete(fifo->sem_mutex);    sem_delete(fifo->sem_full);    sem_delete(fifo->sem_empty);    //分离内存    shmdt(fifo->p_shm);    //删除共享内存    if (shmctl(fifo->shmid, IPC_RMID, NULL) == -1)        err_exit("remove share memory error");    //将fifo内存释放    free(fifo);}
/**将buf内容按照顺序写入共享内存注意此处的P,V操作并没有使用SEM_UNDO标记**/void shmfifo_put(shmfifo_t *fifo, const void *buf){    sem_P(fifo->sem_full);    sem_P(fifo->sem_mutex);    //从结构体中获取写入位置    char *index = fifo->p_payload +                  (fifo->p_shm->wr_index * fifo->p_shm->blksize);    memcpy(index, buf, fifo->p_shm->blksize);    fifo->p_shm->wr_index = (fifo->p_shm->wr_index+1)%fifo->p_shm->blocks;    sem_V(fifo->sem_mutex);    sem_V(fifo->sem_empty);}
/**将共享内存中的内容按照顺序读出到buf注意此处的P,V操作并没有使用SEM_UNDO标记**/void shmfifo_get(shmfifo_t *fifo, void *buf){    sem_P(fifo->sem_empty);    sem_P(fifo->sem_mutex);    //从结构体中获取读出位置    char *index = fifo->p_payload +                  (fifo->p_shm->rd_index * fifo->p_shm->blksize);    memcpy(buf, index, fifo->p_shm->blksize);    fifo->p_shm->rd_index = (fifo->p_shm->rd_index+1)%fifo->p_shm->blocks;    sem_V(fifo->sem_mutex);    sem_V(fifo->sem_full);}
/**测试代码: write.cpp**/struct Student{    char name[32];    int age;};int main(){    shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 15);    Student s;    bzero(&s, sizeof(s));    strcpy(s.name, "xiaofang");    for (int i = 0; i < 15; ++i)    {        sprintf(&(s.name[8]), "%d", i);        s.age = i;        shmfifo_put(fifo, &s);        cout << "put success" << endl;    }}
/**测试代码: read.cpp**/int main(){    shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 3);    Student s;    for (int i = 0; i < 5; ++i)    {        bzero(&s, sizeof(s));        shmfifo_get(fifo, &s);        printf("name: %s, age = %d\n", s.name, s.age);    }    return 0;}
/**测试代码: 销毁所创建的共享内存与信号量, free**/int main(){    shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 3);    shmfifo_destroy(fifo);    return 0;}

完整C源代码:http://download.csdn.net/detail/hanqing280441589/8437855

 

C++版本:

//ShmFifo类设计class ShmFifo{public:    ShmFifo(int _key, int _blksize, int _blocks);    ~ShmFifo();    void put(const void *buf);    void get(void *buf);    void destroy();private:    typedef struct shmhead    {        unsigned int blksize;   //块大小        unsigned int blocks;    //总块数        unsigned int rd_index;  //读索引块        unsigned int wr_index;  //写索引块    } shmhead_t;private:    shmhead_t *p_shm;   //共享内存头部指针    char *p_payload;    //有效负载其实地址    int shmid;      //共享内存ID    int sem_mutex;  //互斥信号量    int sem_full;   //满信号量    int sem_empty;  //空信号量};
/** 构造函数 **/ShmFifo::ShmFifo(int _key, int _blksize, int _blocks){    // 打开一块共享内存    shmid = shmget(_key, 0, 0);    // 如果打开失败, 则表示该共享内存尚未创建, 则创建之    if (shmid == -1)    {        /** 设置共享内存 **/        int size = _blksize*_blocks + sizeof(shmhead_t);        //创建共享内存        shmid = shmget(_key, size, IPC_CREAT|0666);        if (shmid == -1)            err_exit("shmget error");        //创建共享内存成功, 则需要将其连接到进程的地址空间        p_shm = (shmhead_t *)shmat(shmid, NULL, 0);        if (p_shm == (void *) -1)            err_exit("shmat error");        //将共享内存的首部初始化为struct shmhead        p_shm->blksize = _blksize;        p_shm->blocks = _blocks;        p_shm->rd_index = 0;        p_shm->wr_index = 0;        p_payload = (char *)(p_shm+1);        /** 设置三个信号量 **/        sem_mutex = sem_create(_key);        sem_setval(sem_mutex, 1);        sem_full = sem_create(_key+1);        sem_setval(sem_full, _blocks);        sem_empty = sem_create(_key+2);        sem_setval(sem_empty, 0);    }    else    {        //共享内存已经存在, 并且打开成功, 则只需需将其连接到进程的地址空间        p_shm = (shmhead_t *)shmat(shmid, NULL, 0);        if (p_shm == (void *) -1)            err_exit("shmat error");        p_payload = (char *)(p_shm+1);        /** 打开三个信号量 **/        sem_mutex = sem_open(_key);        sem_full = sem_open(_key+1);        sem_empty = sem_open(_key+2);    }}/** 析构函数 **/ShmFifo::~ShmFifo(){    shmdt(p_shm);   //将共享内存卸载    p_shm = NULL;    p_payload = NULL;}
/** destroy函数 **/void ShmFifo::destroy(){    sem_delete(sem_mutex);    sem_delete(sem_full);    sem_delete(sem_empty);    if (shmctl(shmid, IPC_RMID, NULL) == -1)        err_exit("remove share memory error");}
/** put函数 **/void ShmFifo::put(const void *buf){    sem_P(sem_full);    sem_P(sem_mutex);    /** 进入临界区 **/    //从结构体中获取写入位置    char *index = p_payload +                  (p_shm->wr_index * p_shm->blksize);    //写入    memcpy(index, buf, p_shm->blksize);    //index后移    p_shm->wr_index = (p_shm->wr_index+1)%p_shm->blocks;    /** 退出临界区 **/    sem_V(sem_mutex);    sem_V(sem_empty);}
/** get函数 **/void ShmFifo::get(void *buf){    sem_P(sem_empty);    sem_P(sem_mutex);    /** 进入临界区 **/    //从结构体中获取读出位置    char *index = p_payload +                  (p_shm->rd_index * p_shm->blksize);    //读取    memcpy(buf, index, p_shm->blksize);    p_shm->rd_index = (p_shm->rd_index+1)%p_shm->blocks;    /** 退出临界区 **/    sem_V(sem_mutex);    sem_V(sem_full);}

完整C++源代码:http://download.csdn.net/download/hanqing280441589/8438025

 

附-Makefile, 两个程序都可以使用该文件

.PHONY: clean allCC = g++CPPFLAGS = -Wall -gBIN = write read freeSOURCES = $(BIN.=.cpp)all: $(BIN)%.o: %.cpp$(CC) $(CPPFLAGS) -c $^ -o $@write: write.o shmfifo.o ipc.o$(CC) $(CPPFLAGS) $^ -lrt -o $@read: read.o shmfifo.o ipc.o$(CC) $(CPPFLAGS) $^ -lrt -o $@free: free.o shmfifo.o ipc.o$(CC) $(CPPFLAGS) $^ -lrt -o $@clean:-rm -rf $(BIN) *.o bin/ obj/ core

2 0