mmap
是一个系统调用,用于在内存中创建映射区域,将文件或者设备映射到进程的地址空间,从而允许对这些映射区域进行读写操作。通过 mmap
函数,程序可以直接将文件内容映射到内存中,从而避免了频繁的文件 I/O 操作,提高了访问文件数据的效率。此外,mmap
还可以用于创建匿名内存映射,用于进程间通信或者共享内存。
addr
:指定被映射到进程空间内的起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。len
:映射到调用进程地址空间中的字节数。prot
:内存映射区域的保护方式,也即是获取映射地址指针进程的读写执行权限设置。
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
void *mmap(void *addr, size_t len, int prot, int flags,int fd, off_t offset);
取消内存映射,addr是由mmap成功返回的地址,length是要取消的内存长度,munmap 只是将映射的内存从进程的地址空间撤销,如果不调用这个函数,则在进程终止前,该片区域将得不到释放。
#include
int munmap(void *addr, size_t len);
#include
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); //至此就可以摆脱文件描述符参与的工作啦
}
/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)
该文件一个作用是用它作为源,产生一个特定大小的空白文件。*/
在 mmap
函数中,将文件描述符参数设置为 -1
,并指定 MAP_ANONYMOUS
标志,可以实现匿名映射。在这种情况下,mmap
函数不会使用文件,而是直接在内存中创建一个新的匿名映射区域。
优点:写法简单
缺点:只能用于父子进程
void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
优点:可以在没有血缘的进程中通信
缺点:代码相比于之前的微多
功能说明: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
#include /* For mode constants */
#include /* 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
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
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