1. 程式人生 > >IPC通訊之訊息佇列、訊號量和共享記憶體

IPC通訊之訊息佇列、訊號量和共享記憶體

    有三種IPC我們稱作XSI IPC,即訊息佇列,訊號量以及共享儲存器。XSI IPC源自System V的IPC功能。由於XSI IPC不使用檔案系統的名稱空間,而是構造了它們自己的名字空間,為此常常受到批評。

    相似之處:每個核心中的IPC結構都用一個非負整數的識別符號加以引用。例如對一個訊息佇列傳送或取訊息,只需要知道其佇列識別符號。與檔案識別符號不同,IPC識別符號不是小的整數,當一個IPC結構被建立,以後又被刪除時,與這種結構相關的識別符號連續加1,直至達到一個整形數的最大正值,然後又迴轉到0.

    識別符號是IPC物件的內部名,為使多個合作程序能夠在同一IPC物件上會合,需要提供一個外部名方案。為此使用了key,每個IPC物件都與一個key相關連,於是key就用作為該隊向的外部名。

    優缺點:IPC結構是在系統範圍內起作用的,沒有訪問計數。例如,如果程序建立了一個訊息佇列,在該佇列中放入了幾則訊息,然後終止,但是該訊息佇列及其內容並不會被刪除,它們餘留在系統中直至出現下述情況:由某個程序呼叫msgrcv或msgctl讀取訊息或刪除訊息佇列,或某個程序執行ipcrm命令刪除訊息佇列,或由正在再啟動的系統刪除訊息佇列,將此與管道相比,當最後一個訪問管道的程序終止時,管道就被完全地刪除了。對於FIFO而言,雖然當最後一個引用FIFO程序終止時其名字仍保留在系統中,直至顯式地刪除它,但是留在FIFO中的資料卻在此時被全部刪除,於是也就徒有虛名了。
XSI IPC的另一個問題是這些IPC結構在檔案系統中沒有名字,我們不能使用那些使用檔案系統的函式來訪問它們或修改他們的屬性。為了支援它們不得不增加了十幾條全新的系統呼叫(msgget,semop,shmat).我們不能用ls命令見到IPC物件,不能用rm命令刪除他們,不能用chmod命令修改它們的訪問許可權。於是就不得不增加新的命令ipcs和ipcrm.
     因為這些IPC不是用檔案描述符,所以不能對他們使用多路複用I/O函式:epoll/select/poll(這是個致命傷,因為現在很多高效能的伺服器程式設計模型都是使用epoll的,所以這三種通訊機制也只能在一些簡單的場合使用了

)。這就使得難於一次使用多個IPC結構,以及在檔案或裝置I/O中使用IPC結構。例如,沒有某種形式的忙--等待迴圈,就不能使一個伺服器程序等待將要放在兩個訊息佇列任一個中的訊息。
訊息佇列的一個其它優點是:可靠,流是可以控制的,面向記錄,以非先進先出方式處理。

相關API:

訊號量:

它是一個計數器,用於多程序對共享記憶體資料物件的訪問。
#include<sys/sem.h>
int semget(key_t key,int num_sems,int sem_flgs);
int semctl(int sem_id,int sem_num,int command...);
int semop(int sem_id,struct sembuf *sem_ops,size_t num_sem_ops);

共享記憶體:
允許兩個或更多的程序共享一個給定的儲存區,通常,訊號量被用來實現對共享儲存訪問的同步
#include<sys/shm.h>
int shmget(key_t key,size_t size,int shmflag);
void *shmat(int shm_id,const void *shm_addr,int shm_flag);
int shmctl(int shm_id,int cmd,struct shmid_ds *buf);
int shmdt(const void *shm_addr);

訊息佇列:
提供從一個程序向另一個程序傳送一個數據塊的方法(一般新的應用程式不推薦使用)
#include<sys/msg.h>
int msgget(key_t key,int msgflg);
int msgctl(int magid,int cmd,struct msgid_ds *buf);
int msgsnd(int msgid,void *msg_ptr,size_t msg_sz,int msgflag);
int msgrcv(int msgid,void *msg_ptr,size_t msg_sz,long int msg_type,int msgflag);

示例

列印不同型別資料存放的位置

#include <stdio.h>
#include <sys/shm.h>

#define	ARRAY_SIZE	40000
#define	MALLOC_SIZE	100000
#define	SHM_SIZE	100000
#define	SHM_MODE	0600	/* user read/write */

char	array[ARRAY_SIZE];	/* uninitialized data = bss */

int
main(void)
{
	int		shmid;
	char	*ptr, *shmptr;

	printf("array[] from %p to %p\n", (void *)&array[0],
	  (void *)&array[ARRAY_SIZE]);
	printf("stack around %p\n", (void *)&shmid);

	if ((ptr = malloc(MALLOC_SIZE)) == NULL)
		printf("malloc error");
	printf("malloced from %p to %p\n", (void *)ptr,
	  (void *)ptr+MALLOC_SIZE);

	if ((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0)
		printf("shmget error");
	if ((shmptr = shmat(shmid, 0, 0)) == (void *)-1)
		printf("shmat error");
	printf("shared memory attached from %p to %p\n", (void *)shmptr,
	  (void *)shmptr+SHM_SIZE);

	if (shmctl(shmid, IPC_RMID, 0) < 0)
		printf("shmctl error");

	exit(0);
}

mmap函式可將一個檔案的若干部分對映至程序地址空間。這個概念上類似於用shmat函式連線一個共享儲存段。兩者之間的區別是,用mmap對映的儲存段是與檔案關聯的,而XSI共享儲存段並無這種關聯。

更詳細參考: