Linux IPC实践(8) --共享内存/内存映射
概述
? ? 共享內存區是最快的IPC形式。一旦這樣的內存映射到共享它的進程的地址空間,這些進程間數據傳遞不再涉及到內核,換句話說是進程不再通過執行進入內核的系統調用來傳遞彼此的數據(如圖)。
?
共享內存?VS.?其他IPC形式
? ?? 用管道/消息隊列傳遞數據
?
用共享內存傳遞數據
?
? ??共享內存生成之后,傳遞數據并不需要再走Linux內核,共享內存允許兩個或多個進程共享一個給定的存儲區域,數據并不需要在多個進程之間進行復制,因此,共享內存的傳輸速度更快!
mmap內存映射
將文件/設備空間映射到共享內存區
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length);參數:
? ??addr:? 要映射的起始地址,?通常指定為NULL,?讓內核自動選擇;
? ??length: 映射到進程地址空間的字節數;
? ??prot: 映射區保護方式(見下);
? ??flags: 標志(見下);
? ??fd: 文件描述符;
? ??offset: 從文件頭開始的偏移量;
?
prot | 說明 |
PROT_READ | 頁面可讀 |
PROT_WRITE | 頁面可寫 |
PROC_EXEC | 頁面可執行 |
PROC_NONE | 頁面不可訪問 |
?
flags | 說明 |
MAP_SHARED | 變動是共享的 |
MAP_PRIVATE | 變動是私有的 |
MAP_FIXED | 準確解釋addr參數,?如果不指定該參數,?則會以4K大小的內存進行對齊 |
MAP_ANONYMOUS | 建立匿名映射區,?不涉及文件 |
?
mmap返回值:
? ??成功:?返回映射到的內存區的起始地址;
? ??失敗:?返回MAP_FAILED;
?
內存映射示意圖:
(注意:?內存映射時,?是以頁面(4K)作為單位)
/** 示例1: 寫文件映射 將文件以可讀,可寫的方式映射到進程的地址空間, 然后向其中寫入內容 **/ struct Student {char name[4];int age; }; int main(int argc,char **argv) {if (argc != 2)err_quit("usage: ./main <file-name>");int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);if (fd == -1)err_exit("file open error");//為內存映射爭取空間if (lseek(fd, sizeof(Student)*5-1, SEEK_SET) == (off_t)-1)err_exit("lseek error");write(fd, "", 1);Student *p = (Student *)mmap(NULL, sizeof(Student)*5,PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);if (p == MAP_FAILED)err_exit("mmap error");// 此時:操縱文件就可以如同操縱內存一樣了char ch = 'a';for (int i = 0; i < 5; ++i){memcpy((p+i)->name, &ch, 1);(p+i)->age = 20+i;++ ch;}cout << "file initialized!" << endl;if (munmap(p, sizeof(Student)*5) == -1)err_exit("munmap error");cout << "process exit..." << endl;return 0; } /**示例2: 讀文件映射 **/ int main(int argc,char **argv) {if (argc != 2)err_quit("usage: ./main <file-name>");//以只讀方式打開int fd = open(argv[1], O_RDONLY);if (fd == -1)err_exit("file open error");void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);Student *p = (Student *)mmap(NULL, sizeof(Student)*5,PROT_READ, MAP_SHARED, fd, 0);if (p == MAP_FAILED)err_exit("mmap error");// 從內存中讀取數據(其實是從文件中讀取)for (int i = 0; i < 5; ++i){cout << "name: " << (p+i)->name << ", age: " << (p+i)->age << endl;}if (munmap(p, sizeof(Student)*5) == -1)err_exit("munmap error");cout << "process exit..." << endl;return 0; }map注意點:
? ??1.?內存映射不能(也不可能)改變文件的大小;
? ??2.?可用于進程間通信的有效地址空間不完全受限于映射文件的大小,?而應該以內存頁面的大小為準(見下面測試);
? ??3.?文件一旦被映射之后,?所有對映射區域的訪問實際上是對內存區域的訪問;?映射區域內容寫會文件時,?所寫內容不能超過文件的大小.
/** 測試: 注意點2 文件以40K的內容進行創建, 而以120K的內容進行寫回 **/ //程序1: 寫文件映射 int main(int argc,char **argv) {if (argc != 2)err_quit("usage: ./main <file-name>");int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);if (fd == -1)err_exit("file open error");// 注意: 此處我們的文件其實只有40個字節if (lseek(fd, sizeof(Student)*5-1, SEEK_SET) == (off_t)-1)err_exit("lseek error");write(fd, "", 1);// 但是我們卻是以120個字節的方式進行映射Student *p = (Student *)mmap(NULL, sizeof(Student)*15,PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);if (p == MAP_FAILED)err_exit("mmap error");// 以120個字節的方式進行寫入char ch = 'a';for (int i = 0; i < 15; ++i){memcpy((p+i)->name, &ch, 1);(p+i)->age = 20+i;++ ch;}cout << "file initialized!" << endl;// 以120字節的方式卸載該內存區if (munmap(p, sizeof(Student)*15) == -1)err_exit("munmap error");// 注意: 要故意暫停一會兒, 以便讓read程序讀取該共享內存的內容sleep(20);cout << "process exit..." << endl; } //程序2: 讀文件映射 int main(int argc,char **argv) {if (argc != 2)err_quit("usage: ./main <file-name>");//以只讀方式打開int fd = open(argv[1], O_RDONLY);if (fd == -1)err_exit("file open error");void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);// 以120字節的方式映射Student *p = (Student *)mmap(NULL, sizeof(Student)*15,PROT_READ, MAP_SHARED, fd, 0);if (p == MAP_FAILED)err_exit("mmap error");// 以120字節的方式讀取for (int i = 0; i < 15; ++i){cout << "name: " << (p+i)->name << ", age: " << (p+i)->age << endl;}if (munmap(p, sizeof(Student)*15) == -1)err_exit("munmap error");cout << "process exit..." << endl; }msync函數
int msync(void *addr, size_t length, int flags);對映射的共享內存執行同步操作
參數:
? ??addr: 內存起始地址;
? ??length:?長度
? ??flags:?選項
flags | 說明 |
MS_ASYNC | 執行異步寫 |
MS_SYNC | 執行同步寫,?直到內核將數據真正寫入磁盤之后才返回 |
MS_INVALIDATE | 使高速緩存的數據失效 |
返回值:
? ??成功:?返回0;
? ??失敗:?返回-1;
總結
以上是生活随笔為你收集整理的Linux IPC实践(8) --共享内存/内存映射的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android IOS WebRTC 音
- 下一篇: ORACLE MTTR