linux 进程通信之 mmap
一,管道PIPE
二,FIFO通信
三,mmap通信
創(chuàng)建內(nèi)存映射區(qū)。
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int mmap(void *addr, size_t length);函數(shù)mmap:打開(kāi)一個(gè)文件,指定一個(gè)文件的區(qū)域,作為一個(gè)區(qū)域,映射到內(nèi)存中,以后就直接操作那個(gè)內(nèi)存,就能夠?qū)崿F(xiàn)進(jìn)程間的通信。因?yàn)槭莾?nèi)存操作,所以速度最快。
- addr:固定NULL
- length: 拿出文件中的多長(zhǎng)的一段,映射到內(nèi)存。
- offset: 從文件內(nèi)容中的哪個(gè)位置開(kāi)始拿。
- prot
PROT_EXEC Pages may be executed
PROT_READ Pages may be read.
PROT_WRITE Pages may be written
PROT_NONE Pages may not be accessed
- flags
MAP_SHARED: 對(duì)內(nèi)存里的值進(jìn)行修改,會(huì)反映到文件,也就是文件也被修改。
MAP_PRIVATE:對(duì)內(nèi)存里的值進(jìn)行修改,不會(huì)反映到文件,文件不會(huì)被修改。
- offset: 起始位置
- 返回值:
成功:可用的內(nèi)存的首地址
失敗:MAP_FAILED(that is, (void *) -1)
釋放內(nèi)存映射區(qū).
#include <sys/mman.h> int munmap(void *addr, size_t length);- addr: mmap的返回值;
- length: mmap創(chuàng)建的長(zhǎng)度;
- 返回值: 成功0, 失敗-1.
例子:
#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");//釋放映射區(qū)munmap(buf, 8);close(fd); }mmap的七個(gè)問(wèn)題:
- 如果更改上面例子里變量buf的地址,釋放的時(shí)候munmap,還能成功嗎?
不能成功,錯(cuò)誤信息: [Invalid argument]
- 對(duì)映射區(qū)的操作,越界了怎么樣?
open文件size > 要寫入的size > mmap參數(shù)的length: 能夠全部寫入文件。
open文件size < 要寫入的size > mmap參數(shù)的length: 不能全部寫入文件,能夠給寫入的size是open文件的size
- 偏移量隨便填個(gè)數(shù)字會(huì)怎么樣?
mmap函數(shù)出錯(cuò),錯(cuò)誤信息: [Invalid argument]
offset必須是4K的整數(shù)倍,[0, 4*1024......]
用[stat]命令查看文件,發(fā)現(xiàn)文件的size實(shí)際小于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
- 如果文件描述符先關(guān)閉,對(duì)mmap映射有沒(méi)有影響。
沒(méi)有影響。
- open的時(shí)候,可以用新創(chuàng)建一個(gè)文件的方式,來(lái)創(chuàng)建映射區(qū)嗎?
錯(cuò)誤:Bus error(core dumped).
錯(cuò)誤理由是:創(chuàng)建的文件的size為0,所以出上面的錯(cuò)誤。新創(chuàng)建一個(gè)文件后,馬上把文件大小擴(kuò)展一下就不會(huì)發(fā)生錯(cuò)誤了。
int fd = open("mem", O_RDWR|O_CREAT, 0666); ftruncate(fd, 8); //把新創(chuàng)建的文件mem的大小擴(kuò)展為8.- open文件時(shí),選擇O_WRONLY,可以嗎
mmap函數(shù)出錯(cuò),錯(cuò)誤: [Permission denied]。
因?yàn)橐盐募膬?nèi)容讀到內(nèi)存,所以隱含一次讀取操作,所有沒(méi)有讀的權(quán)限的話,就出這個(gè)錯(cuò)誤。
- 當(dāng)選擇MAP_SHARED的時(shí)候,open文件選擇O_RDONLY,prot可以選擇[PROT_READ|PROT_WRITE]嗎
mmap函數(shù)出錯(cuò),錯(cuò)誤: 【Permission denied】.
MAP_SHARED的時(shí)候會(huì)去寫文件,但是open的時(shí)候只有讀權(quán)限,所以權(quán)限不夠。
用mmap實(shí)現(xiàn)父子進(jìn)程間通信的例子:
注意:參數(shù)flags必須是MAP_SHARED,因?yàn)?個(gè)進(jìn)程間通信,需要互相讀寫,所以必須是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); }執(zhí)行結(jié)果:
child:mem=100
parent:mem=100
parent:mem=200
child:mem=200
不知道讀者同學(xué)們發(fā)現(xiàn)了沒(méi)有,用mmap有個(gè)非常雞肋的地方,就是必須要使用一個(gè)文件,其實(shí)這個(gè)文件對(duì)程序沒(méi)有什么作用。所以linux給我們提供了一個(gè)方法,叫做[匿名映射]。
匿名映射:在調(diào)用mmap函數(shù)時(shí)候,在flags參數(shù)那里,設(shè)置[MAP_ANON],并在fd參數(shù)那里設(shè)置[-1]。
int* mem = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);有個(gè)問(wèn)題,在有些unix系統(tǒng)里是沒(méi)有[MAP_ANON][MAP_ANONYMOUS]這2個(gè)宏的,這2個(gè)宏的作用是一樣的,其中一個(gè)是簡(jiǎn)寫。那怎么辦呢?
使用下面2個(gè)文件去映射,因?yàn)橐梦募?#xff0c;所以必須還得有open的調(diào)用,但好處是不用事先做出一個(gè)大小合適的文件了。
- /dev/zero: 可以隨意映射,size無(wú)限大,諢名為[聚寶盆]
- /dev/null:可以隨意映射,size無(wú)限大,但映射完后,文件里不會(huì)存有任何內(nèi)容,所以也叫[無(wú)底洞],一般錯(cuò)誤日志太多,而且不想保留的時(shí)候,會(huì)重定向到這個(gè)文件。
匿名映射的弱點(diǎn):不能實(shí)現(xiàn)無(wú)血緣關(guān)系進(jìn)程間的通信。
用mmap實(shí)現(xiàn)無(wú)血緣關(guān)系的進(jìn)程間通信的例子:
寫入端:
#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 fd;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實(shí)現(xiàn)用多個(gè)進(jìn)程拷貝一個(gè)文件的例子
核心思想:把要拷貝的文件和目標(biāo)文件都映射成內(nèi)存映射區(qū),然后在各自的子進(jìn)程里做拷貝,拷貝時(shí),注意起點(diǎn)和大小。
#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]: 進(jìn)程的數(shù)量 //argv[2]: 要拷貝的文件 int main(int argc, char *argv[]) {if (argc < 3) {printf("bad argv, need 3 arg\n");return -1;}//用stat函數(shù)取得文件的大小struct stat sbuf;int ret = stat(argv[2], &sbuf);if (ret < 0) {perror("stat");return -1;}//文件的大小off_t sz = sbuf.st_size;//余數(shù)off_t yu = sz % atoi(argv[1]);//每個(gè)進(jìn)程拷貝的大小off_t proSz = (sz - yu) / atoi(argv[1]);//open src fileint srcfd = open(argv[2], O_RDONLY);//create target filechar wk[20] = {0};sprintf(wk, "%s.copy", argv[2]);int decfd = open(wk, O_RDWR|O_CREAT|O_TRUNC, 0766);//創(chuàng)建和src文件同等大小的目標(biāo)文件ret = ftruncate(decfd, sz);if (ret < 0) {perror("ftruncate");return -1;}//打開(kāi)要被拷貝文件的內(nèi)存映射區(qū)void *dst = mmap(NULL, sz, PROT_READ, MAP_SHARED, srcfd, 0);if (src == MAP_FAILED) {perror("mmap src:");return -1;}//打開(kāi)要目標(biāo)文件的內(nèi)存映射區(qū)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]);//創(chuàng)建子進(jìn)程,都是這些子進(jìn)程都是本程序的子進(jìn)程for (i=0; i<pCnt; ++i) {//in child processif (fork() == 0) {break;}}//子進(jìn)程的處理if (i < pCnt) {//last timeif (i == pCnt - 1) {memcpy(dst+i*proSz, src+i*proSz, proSz+yu);}else {memcpy(dst+i*proSz, src+i*proSz, proSz);}}//父進(jìn)程的處理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);}}?
總結(jié)
以上是生活随笔為你收集整理的linux 进程通信之 mmap的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux内存管理中的slab分配器
- 下一篇: 11个步骤完美排查Linux机器是否已经