使用消息隊列即可實現消息的先進先出(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就可以了