Linux下IPC方式之共享存儲映射(mmap)

1. 共享存儲映射(mmap)

把文件中的某一段映射到內存上
在這裏插入圖片描述

mmap函數原型:

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

參數:

addr 			建立映射區的首地址,由Linux內核指定。使用時,直接傳遞NULL

length 			映射區長度

prot 
PROT_READ 		可讀
PROT_WRITE		可寫

flags
MAP_SHARED 		共享的,對內存的修改會影響到源文件
MAP_PRIVATE		私有的

fd 				文件描述符

offset			偏移量

返回值
成功 			返回 可用內存的首地址
失敗 			返回 MAP_FAILED

釋放內存區

#include <sys/mman.h>
int munmap(void *addr, size_t length);
addr 	傳mmap返回值

length 	mmap創建的長度

返回值
成功:0; 失敗:-1

在這裏插入圖片描述
實例(MAP_SHARED的作用是,你修改了內存,會影響文件)


#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>
 
int main(){
	int fd=open("men.txt", O_RDWR);
	//創建映射區
	char *mem=mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 
	if(mem==MAP_FAILED){
		perror("mmap err");
		return -1;
	}
 
	//拷貝數據
	strcpy(mem, "hello");
 
	//釋放mmap
	munmap(mem, 8);
	close(fd);
	return 0;
}

2. mmap九問

  1. 如果更改mem變量的地址,釋放的時候munmap,傳入mem還能成功嗎?
  2. 如果對mem越界操作會怎樣?
  3. 如果文件偏移量隨便填個數會怎樣?
  4. 如果文件描述符先關閉,對mmap映射有沒有什麼影響?
  5. open的時候,可以新創建一個文件來創建映射區嗎?
  6. open文件選擇O_WRONLY,可以嗎?
  7. 當選擇MAP_SHARED的時候,open文件選擇O_RDONLY,prot可以選擇PROT_READ|PROT_WRITE嗎?
  8. mmap什麼情況下會報錯?
  9. 如果不判斷返回值會怎麼樣?

1.如果更改mem變量的地址,釋放的時候munmap,傳入mem還能成功嗎?
問題一的測試:(不能改,否則會釋放失敗)

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>
 
int main(){
	int fd=open("men.txt", O_RDWR);
	//創建映射區
	char *mem=mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 
	if(mem==MAP_FAILED){
		perror("mmap err");
		return -1;
	}
 
	//拷貝數據
	strcpy(mem, "hello");
	mem++;
	//如果釋放失敗
	if (munmmap(mem, 8) < 0) {
		perror("munmmap err");
	}
	close(fd);
	return 0;
}

運行結果:
在這裏插入圖片描述
2.如果對mem越界操作會怎樣?
問題二的測試:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>
 
int main(){
	int fd=open("men.txt", O_RDWR);
	//創建映射區
	char *mem=mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 
	if(mem==MAP_FAILED){
		perror("mmap err");
		return -1;
	}
 
	//拷貝數據
	strcpy(mem, "hellollllllllllll");
 
	//釋放內存
	munmmap(mem, 8);
	close(fd);
	return 0;
}

文件的大小對映射區操作有影響,儘量避免。
3.如果文件偏移量隨便填個數會怎樣?

char *mem=mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 1000000000);

在這裏插入圖片描述
offset必須是4k的整數倍。

4.如果文件描述符先關閉,對mmap映射有沒有什麼影響?
沒有。因爲mmap之後,通道就打通了,文件就沒用了。
5.open的時候,可以新創建一個文件來創建映射區嗎?

int fd=open("men.txt", O_RDWR|O_TRUNC, 0664);//創建並截斷文件

如果新創建的文件是空的,會報錯。
如果文件大小不爲0,則可以。

6.open文件選擇O_WRONLY,可以嗎?
不可以,映射到內存的時候隱含一次讀操作,如果只有寫權限,則會報錯
7.當選擇MAP_SHARED的時候,open文件選擇O_RDONLY,prot可以選擇PROT_READ|PROT_WRITE嗎?
不可以。SHARED的時候,映射區的權限要小於等於open文件的權限。

3. mmap實現父子進程通信

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>
 
int main()
{
    // 先創建映射區
    int fd = open("mem.txt",O_RDWR);
    int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    //int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
    if(mem == MAP_FAILED){
        perror("mmap err");
        return -1;
    }
    // fork子進程
    pid_t pid = fork();
 
    // 父進程和子進程交替修改數據
    if(pid == 0 ){
        //son 
        *mem = 100;
        printf("child,*mem = %d\n",*mem);
        sleep(3);
        printf("child,*mem = %d\n",*mem);
    }
    else if(pid > 0){
        //parent
        sleep(1);
        printf("parent,*mem=%d\n",*mem);
        *mem = 1001;
        printf("parent,*mem=%d\n",*mem);
		//回收子進程
        wait(NULL);
    }
 
	//釋放內存
    munmap(mem,4);
    close(fd);
    return 0;
}

運行結果
在這裏插入圖片描述
將上面的註釋去掉,改成


//int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);

運行結果
在這裏插入圖片描述
父進程並沒有讀到子進程的數據,子進程也沒有讀到父進程改的數據。
如果要實現父子進程之間通信,需要將flags設爲MAP_PRIVATE
在這裏插入圖片描述

4. 匿名映射

避免打開文件的操作(上面的例子都有調用open函數)

int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);

使用示例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>
 
int main()
{
    int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
 
    if(mem == MAP_FAILED){
        perror("mmap err");
        return -1;
    }
 
    pid_t pid = fork();
 
    if(pid == 0 ){
        //son 
        *mem = 101;
        printf("child,*mem=%d\n",*mem);
        sleep(3);
        printf("child,*mem=%d\n",*mem);
    }else if(pid > 0){
        //parent 
        sleep(1);
        printf("parent,*mem=%d\n",*mem);
        *mem = 10001;
        printf("parent,*mem=%d\n",*mem);
        wait(NULL);
    }
 
    munmap(mem,4);
    return 0;
}

運行結果
在這裏插入圖片描述
注意:有的Unix系統中沒有MAP_ANON,ANONYMOUS這兩個宏。此時該怎麼辦?
此時用這個
在這裏插入圖片描述
/dev/zero 是一個聚寶盆,無限大,用它做匿名映射,你想取多大都可以。
另外一個,/dev/null 是一個無底洞,一般錯誤信息重定向到這個文件中
小技巧——快速把一個文件頭幾行的數據重定向到另一個文件中(會覆蓋目標文件)
在這裏插入圖片描述

5. mmap實現無血緣進程通信

寫端

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>
 
typedef struct  _Student{
    int sid;
    char sname[20];
}Student;
 
int main(int argc,char *argv[])
{
    if(argc != 2){
        printf("./a.out filename\n");
        return -1;
    }
    
    // 1. open file 
    int fd = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0666);
	//結構體的大小
    int length = sizeof(Student);
	//將文件大小改變爲參數length指定的大小,
	//如果原來的文件大小比參數length大,則超過的部分會被刪除,
	//如果原來的文件大小比參數length小,則文件將被擴展
    ftruncate(fd,length);
 
    // 2. mmap
    Student * stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    //如果不判斷是否出錯,會死的很難看
    if(stu == MAP_FAILED){
        perror("mmap err");
        return -1;
    }
    int num = 1;
 
    // 3. 修改內存數據
    while(1){
        stu->sid = num;
        sprintf(stu->sname,"xiaoming-%03d",num++);
        sleep(1);//相當於每隔1s修改一次映射區的內容
    }
    // 4. 釋放映射區和關閉文件描述符
    munmap(stu,length);
    close(fd);
 
    return 0;
}

讀端

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>
 
typedef struct _Student{
    int sid;
    char sname[20];
}Student;
 
int main(int argc,char *argv[])
{
    //open file 
    int fd = open(argv[1],O_RDWR);
    //mmap 
    int length = sizeof(Student);
    Student *stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(stu == MAP_FAILED){
        perror("mmap err");
        return -1;
    }
    //read data 
    while(1){
        printf("sid=%d,sname=%s\n",stu->sid,stu->sname);
        sleep(1);
    }
    //close and munmap 
    munmap(stu,length);
    close(fd);
    return 0;
}

運行結果:
在這裏插入圖片描述
再開一個讀的:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章