程序間通訊機制有哪些?
程序間的通訊方式:
1.管道(pipe)及有名管道(named pipe):
管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。
有名管道也是半雙工的通訊方式,但是它允許無親緣關係程序間的通訊。
2.訊號(signal):
訊號量是一個計數器,可以用來控制多個程序對共享資源的訪問。它常作為一種鎖機制,防止某程序正在訪問共享資源時,其他程序也訪問該資源。因此,主要作為程序間以及同一程序內不同執行緒之間的同步手段。
3.訊息佇列(message queue):
訊息佇列是訊息的連結表,它克服了上兩種通訊方式中訊號量
其基本思想是:根據”生產者-消費者”原理,利用記憶體中公用訊息緩衝區實現程序之間的資訊交換.
記憶體中開闢了若干訊息緩衝區,用以存放訊息.每當一個程序向另一個程序傳送訊息時,便申請一個訊息緩衝區,並把已準備好的訊息送到緩衝區,然後把該訊息緩衝區插入到接收程序的訊息佇列中,最後通知接收程序.接收程序收到傳送里程發來的通知後,從本程序的訊息佇列中摘下一訊息緩衝區,取出所需的資訊,然後把訊息緩衝區不定期給系統.系統負責管理公用訊息緩衝區以及訊息的傳遞.
一個程序可以給若干個程序傳送訊息,反之,一個程序可以接收不同程序發來的訊息.顯然,程序中關於訊息佇列的操作是臨界區.當傳送程序正往接收程序的訊息佇列中新增一條訊息時,接收程序不能同時從該訊息佇列中到出訊息:反之也一樣.
4.共享記憶體(shared memory):
可以說這是最有用的程序間通訊方式。它使得多個程序可以訪問同一塊記憶體空間,不同程序可以及時看到對方程序中對共享記憶體中資料得更新。這種方式需要依靠某種同步操作,如互斥鎖和訊號量等。
這種通訊模式需要解決兩個問題:第一個問題是怎樣提供共享記憶體;第二個是公共記憶體的互斥關係則是程式開發人員的責任。
5.
主要作為程序之間及同一種程序的不同執行緒之間得同步和互斥手段。
6.套接字(socket);
套解口也是一種程序間通訊機制,與其他通訊機制不同的是,它可用於不同及其間的程序通訊。
用法:
1 管道
它包括無名管道和有名管道兩種,前者用於父程序和子程序間的通訊,後者用於運行於同一臺機器上的任意兩個程序間的通訊。
1.1 無名管道由pipe()函式建立:
#include<unistd.h>
int pipe(int filedis[2]);//
引數filedis返回兩個檔案描述符:filedes[0]為讀而開啟,filedes[1]為寫而開啟。filedes[1]的輸出是filedes[0]的輸入。- //下面的例子示範瞭如何在父程序和子程序間實現通訊。
- #define INPUT 0
#define OUTPUT 1
void main()
{
int file_descriptors[2];
/*定義子程序號 */
pid_t pid;
char buf[256];
int returned_count;
/*建立無名管道*/
pipe(file_descriptors);
/*建立子程序*/
if((pid = fork())==-1){
printf("Error in fork\n");
exit(1);
}
/*執行子程序*/
if(pid ==0){
printf("in the spawned (child) process...\n");
/*子程序向父程序寫資料,關閉管道的讀端*/
close(file_descriptors[INPUT]);
write(file_descriptors[OUTPUT],"test data", strlen("test data"));
exit(0);
}else{
/*執行父程序*/
printf("in the spawning (parent) process...\n");
/*父程序從管道讀取子程序寫的資料,關閉管道的寫端*/
close(file_descriptors[OUTPUT]);
returned_count = read(file_descriptors[INPUT], buf,sizeof(buf));
printf("%d bytes of data received from spawned process: %s\n",
returned_count, buf);
}
}
1.2 有名管道可由兩種方式建立
方式一:mkfifo("myfifo","rw");
方式二:mknod myfifo p
生成了有名管道後,就可以使用一般的檔案I/O函式如open、close、read、write等來對它進行操作。
/* 程序一:讀有名管道*/
#include<stdio.h>
#include<unistd.h>
void main()
{
FILE* in_file;
int count =1;
char buf[80];
in_file = fopen("mypipe","r");
if(in_file == NULL){
printf("Error in fdopen.\n");
exit(1);
}
while((count = fread(buf,1,80, in_file))>0)
printf("received from pipe: %s\n", buf);
fclose(in_file);
}
/* 程序二:寫有名管道*/
#include<stdio.h>
#include<unistd.h>
void main()
{
FILE* out_file;
int count =1;
char buf[80];
out_file = fopen("mypipe","w");
if(out_file == NULL){
printf("Error opening pipe.");
exit(1);
}
sprintf(buf,"this is test data for the named pipe example\n");
fwrite(buf,1,80, out_file);
fclose(out_file);
}
訊息佇列用於運行於同一臺機器上的程序間通訊,它和管道很相似,是一個在系統核心中用來儲存訊息的佇列,它在系統核心中是以訊息連結串列的形式出現。訊息連結串列中節點的結構用msg宣告。
事實上,它是一種正逐漸被淘汰的通訊方式,我們可以用流管道或者套介面的方式來取代它,所以,我們對此方式也不再解釋,也建議讀者忽略這種方式。
3 共享記憶體
共享記憶體是執行在同一臺機器上的程序間通訊最快的方式,因為資料不需要在不同的程序間複製。通常由一個程序建立一塊共享記憶體區,其餘程序對這塊記憶體區進行
讀寫。
得到共享記憶體有兩種方式:對映/dev/mem裝置和記憶體映像檔案。前一種方式不給系統帶來額外的開銷,但在現實中並不常用,因為它控制存取的將是 實際的實體記憶體,
首先要用的函式是shmget,它獲得一個共享儲存識別符號。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);
這個函式有點類似大家熟悉的malloc函式,系統按照請求分配size大小的記憶體用作共享記憶體。
當共享記憶體建立後,其餘程序可以呼叫shmat()將其連線到自身的地址空間中。
void *shmat(int shmid, void *addr, int flag);
shmid為shmget函式返回的共享儲存識別符號,addr和flag引數決定了以什麼方式來確定連線的地址,函式的返回值即是該程序資料段所連線的實際地址,程序可以對此程序進行讀寫操作。
使用共享儲存來實現程序間通訊的注意點是對資料存取的同步,必須確保當一個程序去讀取資料時,它所想要的資料已經寫好了。通常,訊號量被要來實現對共享存 儲資料存取的同步,另外,可以通過使用shmctl函式設定共享儲存記憶體的某些標誌位如SHM_LOCK、SHM_UNLOCK等來實現。
4 訊號量
訊號量又稱為訊號燈,它是用來協調不同程序間的資料物件的,而最主要的應用是前一節的共享記憶體方式的程序間通訊。本質上,訊號量是一個計數器,它用來記錄對某個資源(如共享記憶體)的存取狀況。一般說來,為了獲得共享資源,程序需要執行下列操作:
(1) 測試控制該資源的訊號量。
(2) 若此訊號量的值為正,則允許進行使用該資源。程序將訊號量減1。
(3) 若此訊號量為0,則該資源目前不可用,程序進入睡眠狀態,直至訊號量值大於0,程序被喚醒,轉入步驟(1)。
(4) 當程序不再使用一個訊號量控制的資源時,訊號量值加1。如果此時有程序正在睡眠等待此訊號量,則喚醒此程序。
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semget(key_t key,int nsems,int flag);
struct sem {
short sempid;/* pid of last operaton */
ushort semval;/* current value */
ushort semncnt;/* num procs awaiting increase in semval */
ushort semzcnt;/* num procs awaiting semval = 0 */
- }
key是前面講過的IPC結構的關鍵字,flag將來決定是建立新的訊號量集合,還是引用一個現有的訊號量集合。nsems是該集合中的訊號量數。如果是建立新 集合(一般在伺服器中),則必須指定nsems;如果是引用一個現有的訊號量集合(一般在客戶機中)則將nsems指定為0。
semctl函式用來對訊號量進行操作。
int semctl(int semid, int semnum, int cmd, union semun arg);
不同的操作是通過cmd引數來實現的,在標頭檔案sem.h中定義了7種不同的操作,實際程式設計時可以參照使用。
semop函式自動執行訊號量集合上的運算元組。
int semop(int semid, struct sembuf semoparray[], size_t nops);
semoparray是一個指標,它指向一個訊號量運算元組。nops規定該陣列中操作的數量。
#include<stdio.h>
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
void main()
{
key_t unique_key;/* 定義一個IPC關鍵字*/
int id;
struct sembuf lock_it;
union semun options;
int i;
unique_key = ftok(".",'a');/* 生成關鍵字,字元'a'是一個隨機種子*/
/* 建立一個新的訊號量集合*/
id = semget(unique_key,1, IPC_CREAT | IPC_EXCL |0666);