zoukankan      html  css  js  c++  java
  • Linux IPC实践(13) --System V IPC综合实践

    实践:实现一个先进先出的共享内存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
    ", 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 all
    CC = g++
    CPPFLAGS = -Wall -g
    BIN = write read free
    SOURCES = $(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

  • 相关阅读:
    Codeforces Round #343 (Div. 2) B. Far Relative’s Problem 暴力
    Codeforces Round #343 (Div. 2) A. Far Relative’s Birthday Cake 水题
    Educational Codeforces Round 8 F. Bear and Fair Set 最大流
    Educational Codeforces Round 8 E. Zbazi in Zeydabad 树状数组
    Educational Codeforces Round 8 D. Magic Numbers 数位DP
    Educational Codeforces Round 8 C. Bear and String Distance 贪心
    Educational Codeforces Round 8 B. New Skateboard 暴力
    Educational Codeforces Round 8 A. Tennis Tournament 暴力
    BZOJ 4032: [HEOI2015]最短不公共子串 后缀自动机 暴力
    BZOJ 4031: [HEOI2015]小Z的房间 高斯消元 MartixTree定理 辗转相除法
  • 原文地址:https://www.cnblogs.com/itrena/p/5926954.html
Copyright © 2011-2022 走看看