建立內存映射區。
html
#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);
函數mmap:打開一個文件,指定一個文件的區域,做爲一個區域,映射到內存中,之後就直接操做那個內存,就可以實現進程間的通訊。由於是內存操做,因此速度最快。node
釋放內存映射區。linux
#include <sys/mman.h> int munmap(void *addr, size_t length);
例子:c++
#include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> int main(){ int fd = open("mem", O_RDWR); //char* buf = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); char* buf = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); printf("%s\n", buf); strcpy(buf, "FFFFF"); //釋放映射區 munmap(buf, 8); close(fd); }
mmap的七個問題:shell
若是更改上面例子裏變量buf的地址,釋放的時候munmap,還能成功嗎?微信
不能成功。錯誤信息:【Invalid argument】函數
對映射區的操做,越界了會怎麼樣。學習
偏移量隨便填個數字會怎麼樣。3d
mmap函數出錯,錯誤信息:【Invalid argument】unix
offset必須是4K的整數倍,【0,4*1024。。。】
用【stat】命令查看文件,發現文件的size實際小於4096,可是【IO Block: 4096】
File: pi2.c Size: 442 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 424247 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ ys) Gid: ( 1000/ ys) Access: 2019-05-02 12:54:13.812282158 +0800 Modify: 2019-04-29 13:49:42.489004001 +0800 Change: 2019-04-29 13:49:42.489004001 +0800
若是文件描述符先關閉,對mmap映射有沒有影響。
沒有影響。
open的時候,能夠用新建立一個文件的方式,來建立映射區嗎?
錯誤:Bus error (core dumped)。
錯誤理由是:建立的文件的size爲0,因此出上面的錯誤。新建立一個文件後,立刻把文件大小擴展一下就不會發生錯誤了。
int fd = open("mem", O_RDWR|O_CREAT, 0666); ftruncate(fd, 8);//把新建立的文件mem的大小擴展爲8.
open文件時,選擇O_WRONLY,能夠嗎
mmap函數出錯,錯誤:【Permission denied】。
由於要把文件的內容讀到內存,因此隱含一次讀取操做,因此沒有讀的權限的話,就出這個錯誤。
當選擇MAP_SHARED的時候,open文件選擇O_RDONLY,prot能夠選擇【PROT_READ|PROT_WRITE】嗎
mmap函數出錯,錯誤:【Permission denied】。
MAP_SHARED的時候會去寫文件,可是open的時候只有讀權限,因此權限不夠。
用mmap實現父子進程間通訊的例子:
注意:參數flags必須是MAP_SHARED,由於2個進程間通訊,須要互相讀寫,因此必須是MAP_SHARED
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.h> int main(){ int fd = open("mem", O_RDWR); int* mem = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(mem == MAP_FAILED){ perror("mmap"); return -1; } pid_t pid = fork(); if(pid == 0){ *mem = 100; printf("child:mem=%d\n", *mem); sleep(3); printf("child:mem=%d\n", *mem); } else if(pid > 0){ sleep(1); printf("parent:mem=%d\n", *mem); *mem = 200; printf("parent:mem=%d\n", *mem); wait(NULL); } munmap(mem, 4); close(fd); }
執行結果:
child:mem=100 parent:mem=100 parent:mem=200 child:mem=200
不知道讀者同窗們發現了沒有,用mmap有個很是雞肋的地方,就是必需要使用一個文件,其實這個文件對程序沒有什麼做業。因此linux給咱們提供了一個方法,叫作【匿名映射】。
匿名映射:在調用mmap函數時候,在flags參數那裏,設置【MAP_ANON】,並在fd參數那裏設置【-1】。
int* mem = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
有個問題,在有些unix系統裏是沒有【MAP_ANON】【MAP_ANONYMOUS】這2個宏的,這2個宏的做用是同樣的,其中一個是簡寫。那怎麼辦呢?
使用下面2個文件去映射,由於要用文件,因此必須還得有open的調用,但好處是不用事先作出一個大小合適的文件了。
匿名映射的弱點:不能實現無學員關係進程間的通訊。
用mmap實現無血緣關係的進程間通訊的例子:
寫入端:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.h> typedef struct _student{ int id; char name[20]; }Student; int main(int argc, char* argv[]){ int fd = open("aaa", O_RDWR|O_TRUNC|O_CREAT, 0666); int length = sizeof(Student); ftruncate(fd, length); Student* std = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(std == MAP_FAILED){ perror("mmap"); return -1; } int num = 0; while(1){ std->id = num; sprintf(std->name, "xiaoming-%03d", num++); sleep(1); } munmap(std, length); close(fd); }
讀入端:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.h> typedef struct _student{ int id; char name[20]; }Student; int main(int argc, char* argv[]){ int fd = open("aaa", O_RDWR); int length = sizeof(Student); Student* std = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(std == MAP_FAILED){ perror("mmap"); return -1; } while(1){ printf("id:%03d, name:%s\n", std->id, std->name); sleep(1); } munmap(std, length); close(fd); }
利用mmap實現用多個進程拷貝一個文件的例子
核心思想:把要拷貝的文件和目標文件都映射成內存映射區,而後在各自的子進程裏作拷貝,拷貝時,注意起點和大小。
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/wait.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> //argv[1]:進程的數量 //argv[2]:要拷貝的文件 int main(int argc, char* argv[]){ if(argc < 3){ printf("bad argv,need 3 arg\n"); return -1; } //用stat函數取得文件的大小 struct stat sbuf; int ret = stat(argv[2], &sbuf); if(ret < 0){ perror("stat"); return -1; } //文件的大小 off_t sz = sbuf.st_size; //餘數 off_t yu = sz % atoi(argv[1]); //每一個進程拷貝的大小 off_t proSz = (sz - yu) / atoi(argv[1]); //open src file int srcfd = open(argv[2], O_RDONLY); //create target file char wk[20] = {0}; sprintf(wk, "%s.copy", argv[2]); int decfd = open(wk,O_RDWR|O_CREAT|O_TRUNC, 0766); //建立和src文件同等大小的目標文件 ret = ftruncate(decfd, sz); if(ret < 0){ perror("ftruncate"); return -1; } //打開要被拷貝文件的內存映射區 void* src = mmap(NULL, sz, PROT_READ, MAP_SHARED, srcfd, 0); if(src == MAP_FAILED){ perror("mmap src:"); return -1; } //打開要目標文件的內存映射區 void* dst = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, decfd, 0); if(dst == MAP_FAILED){ perror("mmap dst:"); return -1; } int i; int pCnt = atoi(argv[1]); //建立子進程,都是這些子進程都是本程序的子進程 for(i = 0; i < pCnt; ++i){ //in child process if(fork() == 0) break; } //子進程的處理 if(i < pCnt){ //last time if(i == pCnt - 1){ memcpy(dst+i*proSz, src+i*proSz, proSz+yu); } else{ memcpy(dst+i*proSz, src+i*proSz, proSz); } } //父進程的處理 else{ for(int i = 0; i < pCnt; ++i){ wait(NULL); } //printf("parent pid=%d\n", getpid()); if(munmap(src, sz) == -1){ perror("munmap src"); } if(munmap(dst, sz) == -1){ perror("munmap dsc"); } close(srcfd); close(decfd); } }