三,mmap通信 創建記憶體映射區。 函數mmap:打開一個文件,指定一個文件的區域,作為一個區域,映射到記憶體中,以後就直接操作那個記憶體,就能夠實現進程間的通信。因為是記憶體操作,所以速度最快。 addr:固定NULL length:拿出文件中的多長的一段,映射到記憶體。 offset:從文件內容中的哪個 ...
一,管道PIPE
二,FIFO通信
三,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);
函數mmap:打開一個文件,指定一個文件的區域,作為一個區域,映射到記憶體中,以後就直接操作那個記憶體,就能夠實現進程間的通信。因為是記憶體操作,所以速度最快。
- addr:固定NULL
- length:拿出文件中的多長的一段,映射到記憶體。
- offset:從文件內容中的哪個位置開始拿。
- 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:對記憶體里的值進行修改,會反映到文件,也就是文件也被修改。
- MAP_PRIVATE:對記憶體里的值進行修改,不會反映到文件,文件不會被修改。
- offset:起始位置
- 返回值
- 成功:可用的記憶體的首地址
- 失敗:MAP_FAILED (that is, (void *) -1)
釋放記憶體映射區。
#include <sys/mman.h>
int munmap(void *addr, size_t length);
- addr:mmap的返回值
- length:mmap創建的長度
- 返回值:成功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");
//釋放映射區
munmap(buf, 8);
close(fd);
}
mmap的七個問題:
如果更改上面例子里變數buf的地址,釋放的時候munmap,還能成功嗎?
不能成功。錯誤信息:【Invalid argument】
對映射區的操作,越界了會怎麼樣。
- open文件size > 要寫入的size > mmap參數的length:能夠全部寫入文件。
- open文件size < 要寫入的size > mmap參數的length:不能全部寫入文件,能夠寫入的size是open文件的size
偏移量隨便填個數字會怎麼樣。
mmap函數出錯,錯誤信息:【Invalid argument】
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的調用,但好處是不用事先做出一個大小合適的文件了。
- /dev/zero:可以隨意映射,size無限大,諢名叫【聚寶盆】
- /dev/null:可以隨意映射,size無限大,但映射完後,文件里不會存有任何內容,所以也被叫成【無底洞】,一般錯誤日誌太多,而且不想保留的時候,會重定向到這個文件。
匿名映射的弱點:不能實現無學員關係進程間的通信。
用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]:process count
//argv[2]:src file
int main(int argc, char* argv[]){
if(argc < 3){
printf("bad argv,need 3 arg");
return -1;
}
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]);
printf("proSz:%ld, yu:%ld\n", proSz, yu);
//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, 0666);
ret = ftruncate(decfd, sz);
if(ret < 0){
perror("ftruncate");
return -1;
}
void* vc[atoi(argv[1])];
for(int i = 0; i < atoi(argv[1]); ++i){
vc[i] = mmap(NULL, proSz, PROT_READ|PROT_WRITE, MAP_SHARED, decfd, 0);
if(vc[i] == MAP_FAILED){
perror("mmap die:");
return -1;
}
}
close(srcfd);
close(decfd);
}