内存控制mmap的原型和使用方法
函数原型
// 创建内存映射区
#include <sys/mman.h>
void * mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
// 释放内存映射区
int munmap(void *addr, size_t len);
形参:
- addr:指定映射区域的首地址。通常传入NULL,表示让系统自动分配
- length: 共享内存映射区的大小(小于等于文件的实际大小)
- prot:共享内存映射区的读写属性, PROT_READ, PROT_WRITE
- PROT_READ | PROT_WRITE 读写权限
- flags:标注共享内存的共享属性。MAP_SHARED/ MAP_PRIVARE
- fd:用于创建共享内存映射区那个文件描述符
- offset:默认是0,表示映射文件全部。偏移位置 4K的整数倍
返回值:
- 成功: 返回映射区的首地址
- 失败:MAP_FAILED, 和 errno
mmap的使用例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
void my_mmap(){
char *p = NULL;
int fd;
// 需要先打开一个文件
fd = open("testmap", O_RDWR|O_CREAT|O_TRUNC, 0644);
if (fd==-1){
sys_err("open error");
}
// 需要拓展文件大小, 这样可以拓展文件大小为11
lseek(fd, 10, SEEK_END);
write(fd, "\0", 1);
// 获取文件长度
off_t len = lseek(fd, 0, SEEK_END);
// truncate,可以直接拓展文件大小
ftruncate(fd, 10); // 需要写权限才可以拓展文件大小
p = mmap(NULL, len, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
if (p==MAP_FAILED){
sys_err("mmap error");
}
// 使用p对文件进行读写操作
strcpy(p, "hello mmap"); // 写操作
printf("-----%s\n", p); // 读操作
// 释放mmap空间
int ret = munmap(p, len);
if (ret==-1){
sys_err("munmap error");
}
close(fd);
}
int main() {
my_mmap();
}
mmap的注意事项
- 用于创建映射区的大小为0, 实际指定非0大小创建映射区,会出现总线错误
- 用于创建映射区的文件大小为0, 实际制定0大小创建映射区,出无效参数
- 用于创建映射区的文件读写属性为,只读。映射区属性为读,写。出"无效参数"
- 创建映射区,需要read权限。因此,mmap的权限应该小于等于open权限。
- 文件描述符fd,在mmap创建映射区完成后即可关闭,后续访问文件,用地址访问
- offset必须是4096的整数倍
- 对申请的内存,不能越界访问。
- p++会直接修改p的内存地址,所有在释放的时候回出错,munmap释放的地址,必须是mmap返回的地址
- mmap的返回值必须要进行检查,因为非常容易出错
- 映射访问权限为”私有“MAP_PRIVATE, 对内存所做的所有修改,只在内存有效, 不会反应到物理磁盘上。
- 映射区访问权限为”私有“MAP_PRIVATE,只需要open文件时,有读权限,用于创建映射区即可。
mmap的保险调用方式
- open的时候指定O_RDWR
- mmap(NULL, 有效文件大小, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
mmap父子进程进行通信
- 先创建映射区。open打开
- flags需要使用MAP_SHARED进行共享映射
- 父进程要先进行创建映射区之后在fork创建子进程
- 一个进程读,一个进程写
无血缘关系的进程通信
- 两个进程,打开同一个文件,创建映射区
- 指定flags为MAP_SHARED
- 一个进程写入,一个进程读出
- 注意一点:无血缘关系进程之间通信,mmap:数据可以重复读取,fifo数据只可以读取一次
匿名映射
- 匿名映射flag设置为MAP_ANONYMOUS
- 不需要创建fd文件对象
- len想要多长给多长
- fd传入-1
- 如果呢米有匿名flags的选项
- /dev/zero --> "\0" ,随便取值多少
- /dev/null ----> 随便怎么写入