Mmap参数及解决mmap : invild argument
1. 简介
Mmap设计的目的是为共享内存及映射一个普通文件到内存。(还有一种posix共享内存IPC则纯粹用于共享目的)
共享内存通信有一个好处就是效率比较高,进程只需要读写内存,而不需要管道或消息队列等操作。对于普通文件来说,每次读写都需要进四次的数据复制(对于读来说先读到内核buf,然后再copy到用户空间buf, 写亦然),而共享内存只需要二次(从文件到内存,内存到文件)
2.Mmap&munmap定义
void*mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
intmunmap(void *addr, size_t length);
l addr : 指定文件应被映射到进程空间的起始地址。一般被指定为NULL, 目的为了避免与现有的地址产生冲突。
l Length: 映射到进程地址空间的字节数,从offset偏移开始计算
l Prot : 指定共享内存的访问权限。PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NORE(不可访问)。
l Flag : MAP_SHARED,MAP_PRIVATE,MAP_FIXED等,其中MAP_SHARED和MAP_PRIVATE互斥,MAP_FIXED是为了解决固定addr地址重叠问题,如果发生重叠,则丢弃重叠部分的映射。
l Fd : 即将映射到进程空间的文件描述符,一般由open返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。
l Offset : 从文件的一个偏移量进行映射,这个值必须是PAGE_SIZE的倍数,不然会映射失败。
i. 函数返回值为最后文件映射到进程空间的地址,进程可以直接操作起始地址到length空间。
ii. 使用普通文件提供的内存映射:适用于进任何进程之间。
iii. 使用匿名内存映射: 适用于具有亲缘关系的进程之间,由于父子进程特殊的亲缘关系,在父进程中吊牌用mmap,然后调用fork.那么在调用fork之后,子进程急促继承父进程匿名映射后的地址空间,同样也继承mmap返回的地址,这样,父子进程就可以通过映射区进行通信了.注意,mmap返回的地址,需要由父进程共同维护。
3.在开发使用mmap遇到的问题。(实现写内存,然后同步到文件系统的普通文件中)
n 挂载其他分区到设备上,调用mmap()映射普通文件到内存
n mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
u 调用failed, 返回值为invildargument.具体原因为没有找到,猜测跟page_cache相关,这个没有进行相关分析。
u Debug:去掉PORT_WRITE属性可以成功
将MAP_SHARED换成MAP_PRIVATE可以成功。
这些可以mmap成功,但与最后要实现的功能不符合
另类的解决方法:
利用losetup losetup/dev/loop0 /home/test/file,即将一个普通文件虚拟成一个设备,然后再进行open和mmap,这样就可以成功了。猜测:挂载的那个分区,kernel不能够保证page_cache的一致性,导致mmap失败,如果虚拟成一个设备,那么这个工作会由losetup来完成这个一致性检查与操作,所以可以成功mmap. 这个问题还需要进一步分析源码来解析,以后有空会进行进一步的分析。