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