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