歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux進程間通信(IPC)編程實踐(十一)System V信號量---實現一個先進先出的共享內存shmfifo

Linux進程間通信(IPC)編程實踐(十一)System V信號量---實現一個先進先出的共享內存shmfifo

日期:2017/3/1 12:19:55   编辑:關於Linux

使用消息隊列即可實現消息的先進先出(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++來實現:

//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);  
}  

.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  

需要實時鏈接庫,只需要修改Makefile文件,加上-lrt就可以了

Copyright © Linux教程網 All Rights Reserved