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; //共享內存IDint 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 shmheadfifo->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; //共享內存IDint 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 shmheadp_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總結
以上是生活随笔為你收集整理的Linux IPC实践(13) --System V IPC综合实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 时间同步引起的oracle故障二
- 下一篇: DBN【深度置信网络】【受限玻尔兹曼机深