简介
mmap 是一个系统调用,用于在内存中创建映射区域,将文件或者设备映射到进程的地址空间,从而允许对这些映射区域进行读写操作。通过 mmap 函数,程序可以直接将文件内容映射到内存中,从而避免了频繁的文件 I/O 操作,提高了访问文件数据的效率。此外,mmap 还可以用于创建匿名内存映射,用于进程间通信或者共享内存。
mmap函数
addr:指定被映射到进程空间内的起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。len:映射到调用进程地址空间中的字节数。prot:内存映射区域的保护方式,也即是获取映射地址指针进程的读写执行权限设置。- PROT_EXEC 映射区域可被执行;
- PROT_READ 映射区域可被读取;
- PROT_WRITE 映射区域可被写入;
- PROT_NONE 映射区域不能存取;
flags:MAP_SHARED和MAP_PRIVATE必须指定一个,其他可选:MAP_SHARED: 共享映射,多个进程可以共享同一份映射区域,对映射区的修改会反映到文件中,并且对其他映射该文件的进程也可见。MAP_PRIVATE: 私有映射,映射区的修改不会反映到文件中,而是在进程私有的副本中进行。MAP_ANONYMOUS(或MAP_ANON): 匿名映射,不与任何文件关联,用于创建匿名共享内存或者用作进程间通信的方式之一。MAP_FIXED: 试图将映射区域放置在指定的地址上,如果不能在指定地址上映射,则调用会失败。MAP_NORESERVE: 保留内核空间,但不分配任何物理页面。这允许使用大于实际物理内存的映射空间,但可能导致后续内存分配失败。MAP_LOCKED: 将映射区的所有页锁定到物理内存中,防止被交换出去。MAP_POPULATE: 在映射时预先填充映射区域的内存页,可以减少访问映射区域时的延迟,但可能会导致较长的映射时间。MAP_NONBLOCK: 在MAP_POPULATE模式下,不会阻塞等待内存页的填充完成。MAP_HUGETLB: 使用大页来映射区域。这可以提高性能,特别是对于大型内存映射。MAP_EXECUTABLE: 映射区域可执行。用于将文件映射为可执行代码区域。
fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANON,fd设为-1。offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍(一般是4096的整数倍)。返回值:成功返回映射的内存地址指针,可以用这个地址指针对映射的文件内容进行读写操作,读写文件数据如同操作内存一样;如果 失败则返回NULL。
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags,int fd, off_t offset);
取消内存映射,addr是由mmap成功返回的地址,length是要取消的内存长度,munmap 只是将映射的内存从进程的地址空间撤销,如果不调用这个函数,则在进程终止前,该片区域将得不到释放。
#include <sys/mman.h>
int munmap(void *addr, size_t len);
文件存储映射(零拷贝)
#include <sys/mman.h>
int main()
{
int fd, zero = 0;
fd = open("1.txt", O_RDWR | O_CREAT, 0644);
write(fd, &zero, sizeof(int));
ptr = mmap(NULL, sizeof(int),
PROT_READ | PROT_WRITE | MAP_SHARED,
fd, 0);
/*
这里父子进程同步(信号量)地使用ptr进行数据交换
且退出exit(0)
*/
munmap(ptr,sizeof(int));
close(fd); //至此就可以摆脱文件描述符参与的工作啦
}
匿名内存映射(共享内存)
1.使用 /dev/zero 文件进行匿名映射
优点:可以得到干净的内存
缺点:只能用于父子进程
通过将 /dev/zero 文件映射到内存中,可以获得一个全是零的内存区域,这可以看作是一种匿名映射。
int main()
{
int fd;
ptr = mmap(NULL, sizeof(int),
PROT_READ | PROT_WRITE | MAP_SHARED | MAP_ANON,
-1, 0);
/*
这里父子进程同步(信号量)的使用ptr进行数据交换
且退出exit(0)
*/
}
int main(int argc, char **argv)
{
/*忽略命令行参数处理步骤*/
int fd;
fd = open("/dev/zero", O_RDWR);
ptr = mmap(NULL, sizeof(int),
PROT_READ | PROT_WRITE | MAP_SHARED,
fd, 0);
close(fd);
/*
这里父子进程同步(信号量)地使用ptr进行数据交换
且退出exit(0)
*/
}
/*/dev/zero 是一个特殊的文件,当你读它的时候,
它会提供无限的空字符(NULL, ASCII NUL, 0x00)
该文件一个作用是用它作为源,产生一个特定大小的空白文件。*/
2.不指定文件描述符进行匿名映射
在 mmap 函数中,将文件描述符参数设置为 -1,并指定 MAP_ANONYMOUS 标志,可以实现匿名映射。在这种情况下,mmap 函数不会使用文件,而是直接在内存中创建一个新的匿名映射区域。
优点:写法简单
缺点:只能用于父子进程
void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
3.通过shm_open函数打开共享内存文件
优点:可以在没有血缘的进程中通信
缺点:代码相比于之前的微多
shm_open函数介绍
功能说明:shm_open 用于创建或者打开共享内存文件。笔者认为shm_open 也许仅仅是系统函数open的一个包装,不同之处就是shm_open操作的文件一定是位于tmpfs文件系统里的,常见的Linux发布版的tmpfs文件系统的存放目录就是/dev/shm。
返回值:成功返回fd>0, 失败返回fd<0
参数说明:
-
name:要打开或创建的共享内存文件名,由于shm_open打开或操作的文件都是位于/dev/shm目录的,因此name不能带路径,例如:/var/myshare这样的名称是错误的, -
oflag:打开的文件操作属性:O_CREAT、O_RDWR、O_EXCL的按位或运算组合 -
mode:文件共享模式,例如 0777
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
int shm_open(const char *name, int oflag, mode_t mode);
int shm_unlink(const char *name);
/*
功能说明:删除/dev/shm目录的文件,shm_unlink 删除的文件是由shm_open函数创建于/dev/shm目录的。可以用系统函数unlink来达到同样的效果,用/dev/shm + name 组成完整的路径即可,但一般不要这么做,因为系统的tmpfs的位置也许不是/dev/shm。用shm_open 创建的文件,如果不调用此函数删除,会一直存在于/dev/shm目录里,直到操作系统重启或者调用linux命令rm来删除为止。
*/
示例代码
- 通过
shm_open创建文件 - 通过
ftruncate函数设置文件大小 - 使用
mmap函数进行映射 - 写入/读取问价内容
- 关闭/释放相关资源
//write端
#include <sys/mman.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = shm_open("mmap", O_CREAT | O_RDWR, 0777);
ftruncate(fd, 4);
int* ptr = (int*)mmap(NULL, sizeof(int),
PROT_READ | PROT_WRITE , MAP_SHARED,
fd, 0);
*ptr = 666;
getchar();
close(fd);
munmap(ptr, sizeof(int));
shm_unlink(mmap);
return 0;
}
//read端
#include <sys/mman.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = shm_open("mmap", O_CREAT | O_RDWR, 0777);
ftruncate(fd, 4);
int *ptr = (int *)mmap(NULL, sizeof(int),
PROT_READ | PROT_WRITE, MAP_SHARED,
fd, 0);
printf("*ptr==%d", *ptr);
getchar();
close(fd);
munmap(ptr, sizeof(int));
return 0;
}
输出,成功读取写端写入的内容
root@VM-16-3-ubuntu:~/projects/test# ./read.out
*ptr==666

275

被折叠的 条评论
为什么被折叠?



