Linux下多任務間通訊和同步-mmap共享內存linux
嵌入式開發交流羣280352802,歡迎加入!數據結構
共享內存能夠說是最有用的進程間通訊方式.兩個不用的進程共享內存的意思是:同一塊物理內存被映射到兩個進程的各自的進程地址空間.一個進程能夠及時看到另外一個進程對共享內存的更新,反之亦然.異步
採用共享內存通訊的一個顯而易見的好處效率高,由於進程能夠直接讀寫內存,而不須要任何數據的複製.對於向管道和消息隊列等通訊等方式,則須要在內核和用戶空間進行四次的數據複製,而共享內存則只須要兩次數據複製:一次從輸入文件到共享內存區,另外一個從共享內存區到輸出文件.實際上,進程之間在共享內存時,並不老是讀寫少許數據後就解除映射,有新的通訊時,再從新創建共享內存區域.而是保持共享區域,知道通訊完畢爲止,這樣,數據內容就一直保存在共享內存中,並無寫回文件.共享內存中的內容每每是在解除映射時才寫回文件的.所以,採用共享內存的通訊方式效率很是高.函數
linux從2.2內核開始就支持多種共享內存方式,如mmap系統調用,Posix共享內存,以及System V共享內存.本文主要介紹mmap系統調用的原理及應用.後續的文章會講解System V共享內存.spa
mmap系統調用是的是的進程間經過映射同一個普通文件實現共享內存.普通文件被映射到進程地址空間後,進程能夠向像訪問普通內存同樣對文件進行訪問,沒必要再調用read,write等操做.與mmap系統調用配合使用的系統調用還有munmap,msync等.命令行
實際上,mmap系統調用並非徹底爲了用於共享內存而設計的.它自己提供了不一樣於通常對普通文件的訪問方式,是進程能夠像讀寫內存同樣對普通文件操做.而Posix或System V的共享內存則是純粹用於共享內存的,固然mmap實現共享內存也是主要應用之一.設計
#include <sys/mman.h> void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
各個參數的說明以下:code
函數的返回值爲最後文件映射到進程空間的地址,進程能夠直接操做起始地址爲該值的有效地址.系統調用mmap用於共享內存時有下面兩種經常使用的方式:對象
對於任意的兩個進程可使用第一種方式,而對於具備親緣關係的進程實現共享內存最好的方式應該是採用匿名內存映射的方式.此時,沒必要指定具體的文件,只要設定相應的標誌便可,後面有相應的例子.繼承
該系統調用在進程地址空間中解除一個映射關係,當映射關係解除後,對原來映射地址的訪問將致使段錯誤發生.其函數原型爲:
#include<sys/mman.h> int munmap(void *start, size_t length);
其中,參數addr是調用mmap時返回的地址,len是映射區的大小.
通常來講,進程在映射空間對共享內容的改變並不直接寫回到磁盤文件中,每每在調用munmap後才執行該操做.能夠經過調用msync實現磁盤上文件內容與共享內存區的內容一致.該函數的原型以下:
#include<sys/mman.h> int msync ( void * addr, size_t len, int flags);
addr:文件映射到進程空間的地址;
len:映射空間的大小;
flags:刷新的參數設置,能夠取值MS_ASYNC/MS_SYNC/MS_INVALIDATE
該實例包含兩個子程序,這兩個子程序編譯爲mmap_read和mmap_write.兩個程序經過命令行參數指定痛一個文件來實現共享內存方式的進程間通訊.mmap_write試圖打開命令行參數指定的一個普通文件,把該文件映射到進程的地址空間,而後對映射後的地址空間進行寫操做.mmap_read把命令行參數指定的文件映射到進程的地址空間,而後對映射的地址空間執行讀操做.這樣,兩個進程經過命令行參數指定同一個文件來實現共享內存方式的進程間通訊.寫操做操做子程序源碼以下:
/**************************************************************************************/ /*簡介:mmap_write共享內存,寫操做子程序 */ /*************************************************************************************/ #include <sys/mman.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> typedef struct { char name[4]; int age; }people; int main(int argc, char** argv) { int fd,i; people *p_map; char name[4]; if (argc != 2) { perror("usage: mmap_write <mmap file>"); return 1; } fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777); lseek(fd,sizeof(people)*5-1,SEEK_SET); write(fd,"",1); p_map = (people*) mmap( NULL,sizeof(people)*10,\ PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 ); close( fd ); name[0] = 'a'; name[1] = '\0'; for(i=0; i<10; i++) { name[0] ++; memcpy( ( *(p_map+i) ).name, &name,sizeof(name) ); ( *(p_map+i) ).age = 20+i; } printf(" initialize over \n "); sleep(10); munmap( p_map, sizeof(people)*10 ); printf( "umap ok \n" ); return 0; }
在上面的程序中,首先定義了一個people的數據格式(共享內存區的數據每每有固定的格式,由通訊的各個進程決定,採用結構的方式具備廣泛表明性).mmap_write首先打開或建立一個文件,並把文件的長度設爲5個people結構大小.而後從mmap的返回地址開始,設置了10個people結構.而後進程睡眠10s,等待其餘進程映射同一個文件,最後解除映射.讀操做子程序的源代碼以下:
/**************************************************************************************/ /*簡介:mmap_read共享內存,讀操做子程序 */ /*************************************************************************************/ #include <sys/mman.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> typedef struct { char name[4]; int age; }people; int main(int argc, char** argv) { int fd,i; people *p_map; if (argc != 2) { perror("usage: mmap_read <mmap file>"); return 1; } fd=open( argv[1],O_CREAT|O_RDWR,00777 ); p_map = (people*)mmap(NULL,sizeof(people)*10,\ PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); close(fd); for(i = 0;i<10;i++) { printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age ); } munmap( p_map,sizeof(people)*10 ); return 0; }
mmap_read只是簡單的映射一個文件,並以people數據結構的格式從mmap返回的地址處讀取10個people結構,並輸出讀取的值,而後解除映射.下圖是運行結果.
文件被映射後,調用mmap的進程對返回地址的訪問實際上是對某一內存區域的訪問,暫時脫離了磁盤上文件的影響.全部對mmap返回地址空間的操做只是在內存中才有意義,只有在調用了munmap或或者msync時,才把內存中的相應內容寫回磁盤文件.