抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

内存映射

内存映射(Memory-mapped I/O)是将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件

image-20220429194804118

父子进程间通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*************************************************************************
> File Name: mmap-parent-child-ipc.c
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年05月15日 星期日 21时25分03秒
************************************************************************/


/*
* #include <sys/mman.h>
* void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
* - 功能: 将一个文件或者设备的数据映射到内存中
* - 参数:
* - void *addr : NULL // 由内核指定
* - length : 要映射的数据长度, 这个值不能为0,建议使用文件的长度。
* 获取文件长度 : stat、lseek
* - prot : 对申请的内存映
* - PROT_EXEC : 可执行的权限
* - PROT_READ : 读权限
* - PROT_WRITE : 写权限
* - PROT_NONE : 没有权限
* 要操作映射内存,必须要有读的权限
* PROT_READ、PROT_READ|PROT_WRITE
* - flags :
* - MAP_SHARED : 映射区的数据会自动和磁盘文件进行同步,进程间通信,必 需要设置这个选项
* - MAP_PRIVATE : 不同步,内存映射器的数据改变了,对原的文件不会修改, 重新创建一个新的文件。(copy on write)
* - fd: 需要映射的文件描述符
* - 通过open得到,open的是一个磁盘文件
* - 注意: 文件的大小不能为0, open指定的权限不能和prot参数有冲突。
* open 只读/读写 prot : PROT_READ(必须有)
* prot : PROT_READ open:只读/读写 (小于open的权限)
* prot : PROT_READ | PROT_WRITE open: 只能是读写
* - offset: 便宜量, 一般不用。必须指定的是4k的正数倍。0表示不偏移
* - 返回值: 返回创建的内存的首地址
* 失败返回MAP_FAILED, (void *) -1
* int munmap(void *addr, size_t length);
* - 功能: 释放内存映射
* - 参数:
* - addr : 要释放的内存的首地址
* - length : 要释放的内存大小, 要和 mmap 函数中的length参数的值一样
*
*/

/*
* 使用内存映射实现进程通信:
* 1.有关系的进程(父子进程)
* - 还有没有子进程的时候
* - 通过唯一的父进程, 先创建内存映射区
* - 有了内存映射区以后, 创建子进程
* - 父子进程共享创建的内存映射区
*
* 2.没有关系的进程通信
* - 准备一个大小不是0的磁盘文件
* - 进程 1 通过磁盘文件创建内存映射区
* - 得到一个操作这块内存的指针
* - 进程 2 通过磁盘文件创建内存映射区
* - 得到一个操作这块内存的指针
* - 使用内存映射区实现进程通信
* 注意: 内存映射区通信,是非阻塞的
*/

#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>

int main() {

// 1. 打开一个文件
int fd = open("test.txt", O_RDWR);
int size = lseek(fd, 0, SEEK_END); // 获取文件的大小

// 2.创建文件 mmap(地址、大小、权限和open对应起来、同步、文件描述符、偏移量)
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(ptr == MAP_FAILED) {
perror("mmap");
exit(0);
}

// 创建子进程
pid_t pid = fork();
if(pid > 0) {
wait(NULL);
// 父进程
char buf[64];
strcpy(buf, (char*)ptr);
printf("read data : %s\n", buf);
} else if(pid == 0){
// 子进程
strcpy((char *)ptr, "nihao a, son !!!");
}


// 关闭内存映射区
munmap(ptr, size);
return 0;
}

内存映射的注意事项

  • 如果对mmap的返回值(ptr)做++操作(ptr++),munmap是否能够成功?

​ void *ptr = mmap(…)

​ ptr++; 可以对其进行++ 操作

​ 但是 munmap(ptr, len) // 错误,要保存地址

  • 如果open时O_RDONLY,mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?

​ 错误,返回一个宏 MAP_FAILED

​ open() 函数中的权限建议和prot参数的权限保持一致

  • 如果文件偏移量为1000会怎样?

    偏移量是4k的整数倍,返回 MAP_FAILED

  • mmap什么情况下会调用失败?

​ - 第二个参数: length = 0 (映射数据长度) 不能再内存中映射为 0

​ - 第三个参数: prot

​ 只指定了写权限

​ prot PROT_READ | PROT_WRITE

​ 第5个参数(文件描述符) fd 通过open函数时指定的 O_RDONLY 或者 O_WRONLY

  • 可以open的时候O_CREAT一个新文件来创建映射区吗?

​ 可以的,但文件的大小如果为0的话,肯定不行

​ 可以对新的文件进行扩展

​ lseek()

​ truncate()

  • mmap后关闭文件描述符,对mmap映射有没有影响?

    int fd = open(“xxx”);

    mmap(…, fd, 0);

    close(fd); // 关闭不会产生问题

    映射区还存在,创建映射区的fd被关闭,没有任何影响

  • 对ptr越界操作会怎样?

​ void * ptr = mmap(null, 100, …)

​ 4k

​ 越界操作的是非法的内存 -> 段错误

使用内存映射实现文件拷贝

需要先创建一个test.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*************************************************************************
> File Name: copy.c
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年05月18日 星期三 10时47分03秒
************************************************************************/

/*
* 思路:
* 1.对原始的文件进行内存映射
* 2.创建一个新文件 (拓展该文件)
* 3.把新文件的数据映射要内存中
* 4.通过内存拷贝将第一个文件的内存数据拷贝到新的文件内存中
* 5.释放资源
*
*/

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

// 使用内存映射实现文件拷贝
int main() {

// 对原始的文件进行内存映射
int fd = open("test.txt", O_RDWR);
if(fd == -1) {
perror("open");
exit(-1);
}

// 获取原始文件大小
int len = lseek(fd, 0, SEEK_END);

// 创建一个新文件
int fd1 = open("cpy.txt", O_RDWR | O_CREAT, 0664);

// 对新创建文件进程拓展
truncate("cpy.txt", len);
write(fd1, " ", 1);

// 内存映射
void *ptr = mmap(NULL, len, PROT_READ| PROT_WRITE, MAP_SHARED, fd, 0);
void *ptr1 = mmap(NULL, len, PROT_READ| PROT_WRITE, MAP_SHARED, fd1, 0);

if(ptr == MAP_FAILED) {
perror("mmap");
exit(-1);
}

if(ptr1 == MAP_FAILED) {
perror("mmap");
exit(-1);
}

// 内存拷贝
memcpy(ptr1, ptr, len);

// 释放资源
munmap(ptr, len);
munmap(ptr1, len);

close(fd1);
close(fd);

return 0;
}

匿名映射

只能做父子进程中的通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*************************************************************************
> File Name: mmap-anon.c
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年05月18日 星期三 13时10分14秒
************************************************************************/

/*
* 匿名映射: 不需要文件实体进程一个内存映射
*/

#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int main() {

// 创建匿名映射区
int len = 4069;
void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if(ptr == MAP_FAILED) {
perror("mmap");
exit(-1);
}

// 父子进程间通信
pid_t pid = fork();
if(pid > 0) {
// 父
strcpy((char *)ptr, "hello");
wait(NULL);
} else if(pid == 0) {
// 子
sleep(1);
printf("%s\n", (char *)ptr);
}

int ret = munmap(ptr, len);
if(ret == -1) {
perror("munmap");
exit(-1);
}

return 0;
}

评论