全部博文(23)
2014年(23)
分类: LINUX
2014-09-21 15:17:56
首先,文件映射是虚存的中心概念, 文 件映射一方面给用户提供了一组措施, 好似用户将文件映射到自己地址空间的某个部分, 使用简单的内存访问指令读写文件;另一方面, 它也可以用于内核的 基本组织模式, 在这种模式种, 内核将整个地址空间视为诸如文件之类的一组不同对象的映射. 中的传统文件访问方式是, 首先用open系统调用打开文 件, 然后使用read, write以及lseek等调用进行顺序或者随即的I/O. 这种方式是非常低效的, 每一次I/O操作都需要一次系统调 用. 另外, 如果若干个进程访问同一个文件, 每个进程都要在自己的地址空间维护一个副本, 浪费了内存空间. 而如果能够通过一定的机制将页面映射到 进程的地址空间中, 也就是说首先通过简单的产生某些内存管理数据结构完成映射的创建. 当进程访问页面时产生一个缺页中断, 内核将页面读入内存并且更 新页表指向该页面. 而且这种方式非常方便于同一副本的共享.
接下来,我们来看下在linux内核中mmap的函数原型:void *mmap(void *addr,size_t length ,int prot, int flags, int fd, off_t offset);
其中,addr是映射区起始地址,通常设为NULL,由系统指定。
length:将文件的多大长度映射到内存
prot:映射区的保护方式,可以是:
PROT_EXEC:映射区可被执行 PROT_READ:映射区可被读取
PROT_WRITE:映射区可被写入 PROT_NONE:映射区不能存取
flag:映射区的特性,可以是:
MAP_SHARD:对映射区的写入数据会复制回文件,且允许其他映射该文件的进程共享
MAP_PRIVATE:对映射区域的写入数据会产生一个映射的复制(copy-on-write),对此区域所做的修改不会写回到原文件
其他标识这里就不再写出来了,可以通过man mmap查看
fd:由open返回的文件描述符,代笔要映射的文件
offset:以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映射
下面主要是分析有关mmap系统调用的实现过程:
1.先通过文件系统定位要映射的文件;
2.权限检查, 映射的权限不会超过文件打开的方式, 也就是说如果文件是以只读方式打开, 那么则不允许建立一个可写映射;
3.创建一个vma对象, 并对之进行初始化;
4.调用映射文件的mmap函数, 其主要工作是给vm_ops向量表赋值;
5.把该vma链入该进程的vma链表中, 如果可以和前后的vma合并则合并;
6.如果是要求VM_LOCKED(映射区不被换出)方式映射, 则发出缺页请求, 把映射页面读入内存中.
下面此图来次《Unix Network programming》卷二12.2节,对mmap有个比较深刻的印象
附加:mmap的应用源代码示例(把文件映射到内存)
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int fd;
char *mapped_mem, * p;
int flength = 1024;
void * start_addr = 0;
fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
flength = lseek(fd, 1, SEEK_END);
write(fd, "\0", 1); /* 在文件最后添加一个空字符,以便下面printf正常工作 */
lseek(fd, 0, SEEK_SET);
mapped_mem = mmap(start_addr, flength, PROT_READ, MAP_PRIVATE, fd, 0); //允许读,不允许其它进程访问此内存区域
/* 使用映射区域. */
printf("%s\n", mapped_mem); /* 为了保证这里工作正常,参数传递的文件名最好是一个文本文件 */
close(fd);
munmap(mapped_mem, flength);
return 0;
}
编译运行此程序:
gcc -o map mmap.c
./map file_name