Linux 進程間通訊(IPC)方式有以下幾種: 1-》管道(pipe)和有名管道(fifo). 2-》訊息佇列 3-》共用記憶體 4-》訊號量 5-》訊號(signal) 6-》通訊端(sicket) 在這裡我們看一下第3種====共用記憶體(share memory )。其它通訊方式見其它文章。 顧名思義:共用記憶體就是兩個或多個進程共用一塊記憶體地區。 這種通訊方式允許兩個不相關的進程能夠訪問處理同一塊記憶體地區。從而達到進程間資料的交換和處理等。在這些進行通訊方式中,共用記憶體是一種非常高效的通訊方式,一種最快的通訊方式。為什麼快呢?只要我們向一個進程匯總寫入的資料,則共用這個記憶體地區的所有進程都可見,從而達到提醒的目的。可以歸為一句話:一處改變,隨處可見。 原理: 共用記憶體是被多個進程共用的一塊實體記憶體,而這些記憶體就是把共用記憶體區域對應到自己的虛擬記憶體中。從而對共用記憶體進行操作。 ================================================ 共用記憶體比pipe的優勢: 1.用在任何進程,不比考慮進程關係, 2.讀走後還存在資料 3.可以隨機讀取所需要的資料 ============================================= 接下來我們看一下關於共用記憶體的操作函數: 主要有:shmget();shmat();shmdt();shmctl(); 在使用這些函數需引進標頭檔#include<sys/ipc.h>和#include<sys/shm.h> 我們都知道在電腦的世界裡什麼都必須先聲明後使用,對此也不例外。 要實現共用記憶體通訊,必須現有共用記憶體的地區。 下面我們看一下shmget函數。 函數原型:int shmget(key_t _key, size_t _size, int _shmflg); 功能:建立共用記憶體 傳回值:返回共用記憶體的標識碼。失敗返回-1。 參數說明: _key :標識共用記憶體的索引值。有IPC _PRIVATE選項表示該記憶體塊為該進程私人,一般不用。 _size :表示要建立共用記憶體的長度。我們一般用getpagesize();函數來設定,此函數的傳回值為4096個位元組。 _shmflg: 標誌,是許可權或建立方式。 建立方式有:IPC_CREAT如果記憶體不存在則建立一個共用記憶體,否則開啟。 IPC_EXCL 只有共用記憶體不存在的時候,新的共用記憶體才會被建立,否則會產生錯誤。 下面我們簡單地看一個小例子: /* demo shmfet */ #include<stdio.h> #include<fcntl.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/shm.h> #include <unistd.h> int main(int argc ,char *argv[]) { int shmid; shmid = shmget(IPC_PRIVATE ,getpagesize(),IPC_CREAT | IPC_EXCL | 0666); //getpagesize() return 4096. if(shmid == -1) { perror("shmget error:"); exit(EXIT_FAILURE); } printf("shmid=%d\n",shmid);//在此我們列印建立的記憶體表識ID return 0; } 運行結果為:我們可以用ipcs -m(查看系統共用記憶體塊資訊 的命令)來查看是否被建立成功。
當然也可以刪除刪除共用記憶體命令為: ipcrm shm shmid列的列號(共用記憶體的標識ID)。 eg: ipcrm shm 4816896 通常情況下,該_key值通過ftok函數得到,來產生唯一的KEY值ID int key_t key; key=ftok("/etc/passwd",1234); int shmid=shmget(key,getpagesize(),IPC_CREAT | IPC_EXCL | 0666); 註: #include <sys/types.h> #include <sys/ipc.h> 如下: key_t ftok( char * fname, int id ) fname就是你指定的檔案名稱(已經存在的檔案名稱),一般使用目前的目錄,如: key_t key; key = ftok(".", 1); 這樣就是將fname設為目前的目錄。 id是子序號。 在一般的UNIX實現中,是將檔案的索引節點號取出,前面加上子序號得到key_t的傳回值。 如指定檔案的索引節點號為65538,換算成16進位為0x010002,而你指定的ID值為38,換算成16進位為0x26,則最後的key_t傳回值為0x26010002。 查詢檔案索引節點號的方法是: ls -i 當刪除重建檔案後,索引節點號由作業系統根據當時檔案系統的使用方式分配,因此與原來不同,所以得到的索引節點號也不同。 如果要確保key_t值不變,要麼確保ftok的檔案不被刪除,要麼不用ftok,指定一個固定的key_t值 int shmid=shmget(6666 ,getpagesize(),IPC_CREAT | IPC_EXCL | 0666); 下面看個例子: /* demo shmfet 2 */ #include<stdio.h> #include<fcntl.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/shm.h> #include <unistd.h> int main(int argc ,char *argv[]) { int shmid; int key_t key; key=ftok(“.”,1234); Shmid=shmget(key ,getpagesize(),IPC_CREAT | IPC_EXCL | 0666); //getpagesize() return 4096. if(shmid == -1) { perror("shmget error:"); exit(EXIT_FAILURE); } printf("shmid=%d\n",shmid);//在此我們列印建立的記憶體表識ID return 0; } 運行結果: 會提示file exits 因為我們已經指定一個固定的key_t值。
==================================================================== 下面我們看第二個函數 #include<sys/shm.h> #include<sys/ipc.h> void *shmat(int shmid, const void *shmaddr, int shmflg); 功能:允許進程訪問共用記憶體。 傳回值:成功返回共用記憶體的起始地址,失敗返回-1. 參數: _Shmid 共用記憶體的ID ,也就是shmget();函數的傳回值。 _Shmaddr 共用記憶體的起始地址。一般指定為0. _ shmflg 是本進程對共用記憶體的操作模式。一般指定為0,即可讀可寫。 如果指定為SHM_RDONLY 的話就是唯讀模式。 提示:後賣弄我們會看個例子。 下面我們看第3個函數 使用完了記憶體我們要進行釋放操作。 #include<sys/shm.h> #include<sys/ipc.h> Int shmdt( _const void *_shmaddr); 功能:釋放共用記憶體,雖然釋放不用了,但是共用記憶體仍然存在。 參數:共用記憶體的起始地址;就是shmat();函數的傳回值。 傳回值:成功返回0,失敗返回-1. 下面我們看第4個函數; #include<sys/shm.h> #include<sys/ipc.h> Int shmctl(int _shmid,int _cmd ,struct shmid _ds *buf); 功能:共用記憶體的控制函數 傳回值:成功返回0,失敗返回-1. 參數:_shmid 共用記憶體的ID。 _cmd 允許的存在,最常用的有IPC_RMID 刪除共用記憶體的記憶體段。 buf 不錯記憶體模式狀態和存取權限的資料結構,通常為0. ===================================================================================================== Ok,看例子,讓程式驗證真理: 我們來實現BMI 指數:身體質量指數來衡量肥胖我們以 20~25為正常 <20 偏瘦 >25偏胖計算,可能不是這個值。但主要是通訊(*^__^*)嘻嘻…… =================================================================================================== 源檔案1: #include<stdio.h> #include<sys/ipc.h> #include<sys/shm.h> #include<stdlib.h> int main(int argc ,char *argv[]) { int shmid; float *addr; float h,w,flag=1.0f;//flag 作為判斷迴圈的標誌 shmid= shmget(ftok("./file",1000),getpagesize(),IPC_CREAT | IPC_EXCL | 0666); if(shmid==-1) { perror("shmget error:"); exit(EXIT_FAILURE); } addr=shmat(shmid,0,0); if(-1==*addr) { perror("shmat error:"); exit(EXIT_FAILURE); } while(1) { printf(" enter you height(CM) and height(KG):\n"); scanf("%f%f",addr,addr+1); *(addr+2)=flag; if(*addr==0||*(addr+1)==0 ) break; //if 輸入其中一個的為0,跳出迴圈結束程式 } return 0; } 源檔案2: #include<stdio.h> #include<sys/ipc.h> #include<sys/shm.h> #include<stdlib.h> int main(int argc ,char *argv[]) { int shmid; float *addr; float h,w; shmid= shmget(ftok("./file",1000),getpagesize(), 0666); if(shmid==-1) { perror("shmget error:"); exit(EXIT_FAILURE); } addr =shmat(shmid,0,0); if(-1== *addr) { perror("shmat error:"); exit(EXIT_FAILURE); } while(1) { if(*(addr+2)!=1.0) //如果還沒有輸入新的資料那麼就跳過此次迴圈,直到再次輸入新的資料。 { sleep(1); continue; } *(addr+2)=0; h=*addr; w=*(addr+1); if(h==0 || w==0) break; //if 輸入其中一個的為0,跳出迴圈結束程式 int ret=w/(h*h/10000); if(ret>=20 &&ret <=25) { printf("ok!\n"); }else if(ret <20) { printf("thin!\n"); } else { printf("fat!\n"); } } if(-1==shmdt(addr)) { perror("shmdt error:"); exit(EXIT_FAILURE); } if (shmctl(shmid,IPC_RMID,0)==-1) { perror("shctl error:"); exit(EXIT_FAILURE); } return 0; } 運行結果,當我們在一個終端上輸入時,在另一個終端會列印出結果,而當我們輸入0,0時兩個都會退出.
|