注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

多进程mmap同一个文件的一致性  

2011-09-07 22:17:39|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、问题的引出

今天一个同事问了一个问题,就是当多个文件通过mmap映射同一个文件的时候,它们看到的物理页面(因此最终导致的也就是对应的真实文件)内容应该是相同的,但是另一个方面,每个进程都有自己的地址空间,它们的页表是不共享的,它们是如何实现系统级的共享的?

二、mmap的实现

核心思想:每个文件的绝对路径在系统中是唯一的(即使是相对路径文件转换为绝对路径之后这个绝对路径也在系统中是唯一的),通过这个唯一的绝对路径,我们可以确定一个系统中唯一的inode节点。这个inode节点是系统唯一的,并且和文件一一对应。所有的mmap文件将会在这个inode节点级别实现更底层信息的共享。关于这一点,我在后来看毛德操的《情景分析》中看到了作者类似的说明,这是后话,相关章节在第一版(如果有多个版本的话)第5.6节“文件的写与读”,有兴趣的同学可以看一下,毛老师的说明比我高屋建瓴很多。

1、mmap系统调用的实现

对于x86系统的系统调用的跳转表位于linux-2.6.37.1\arch\x86\kernel\syscall_table_32.S文件中,其中可以看到对于mmap系统调用转发的函数位于(linux-2.6.37.1\arch\x86\include\asm\unistd_32.h #define __NR_mmap2  192
)

.long ptregs_vfork /* 190 */
 .long sys_getrlimit
 .long sys_mmap_pgoff
 .long sys_truncate64
 .long sys_ftruncate64
 .long sys_stat64 /* 195 */

linux-2.6.37.1\mm\mmap.c

SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
  unsigned long, prot, unsigned long, flags,
  unsigned long, fd, unsigned long, pgoff)


 if (!(flags & MAP_ANONYMOUS)) {如果明确指明了使用匿名页面,则不进行文件描述符的任何特殊操作
  audit_mmap_fd(fd, flags);
  if (unlikely(flags & MAP_HUGETLB))
   return -EINVAL;
  file = fget(fd);通过fd找到对应的struct file结构
  if (!file)
   goto out;
 }

do_mmap_pgoff-->>mmap_region-->>file->f_op->mmap(file, vma);

也就是最终调用了这个文件结构中指定的mmap接口,那么我们就以ext2文件系统为例看一下这个实现。

2、ext2的mmap的实现

linux-2.6.37.1\fs\ext2\namei.c

{
  inode->i_mapping->a_ops = &ext2_aops;
  inode->i_fop = &ext2_file_operations; 这个函数跳转表中将会初始化ext2文件使用的mmap接口,见下面的ext2_file_operations内容
 }

这里注册的一个新节点的i_fop注册了一个文件的操作函数

const struct file_operations ext2_file_operations = {
 .llseek  = generic_file_llseek,
 .read  = do_sync_read,
 .write  = do_sync_write,
 .aio_read = generic_file_aio_read,
 .aio_write = generic_file_aio_write,
 .unlocked_ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
 .compat_ioctl = ext2_compat_ioctl,
#endif
 .mmap  = generic_file_mmap, 用户API执行的mmap将首先在此处转发

const struct vm_operations_struct generic_file_vm_ops = {
 .fault  = filemap_fault,
};
/* This is used for a general mmap of a disk file */

int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
{
 struct address_space *mapping = file->f_mapping;

 if (!mapping->a_ops->readpage)
  return -ENOEXEC;
 file_accessed(file);
 vma->vm_ops = &generic_file_vm_ops;这里为这个mmap映射的VMA区间注册特殊的vm_ops操作,虽然这里只是一个通用的文件操作,但是这个通用也是以专有结构为基础的
 vma->vm_flags |= VM_CAN_NONLINEAR;
 return 0;
}
3、当一个进程缺页异常发生时

linux-2.6.37.1\arch\x86\mm\fault.c

do_page_fault---->>>handle_mm_fault----->>>handle_pte_fault

if (!pte_present(entry)) {
  if (pte_none(entry)) {
   if (vma->vm_ops) {
    if (likely(vma->vm_ops->fault))这里判断即时我们刚才注册的generic_file_vm_ops结构,所以非空。
     return do_linear_fault(mm, vma, address,
      pte, pmd, flags, entry);
   }
   return do_anonymous_page(mm, vma, address,
       pte, pmd, flags);
  }

do_linear_fault----->>>__do_fault---->>>ret = vma->vm_ops->fault(vma, &vmf);此处调用我们注册的fault函数,也就是我们看到的generic_file_vm_ops-->>>filemap_fault

4、filemap_fault函数的处理

int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
 int error;
 struct file *file = vma->vm_file;
 struct address_space *mapping = file->f_mapping;由于刚才说系统中同一个文件的inode相同,所以此时所有的mmap将会看到相同的一个address_space,进而导致看到的物理内容相同
 struct file_ra_state *ra = &file->f_ra;
 struct inode *inode = mapping->host;
 pgoff_t offset = vmf->pgoff;
 struct page *page;
 pgoff_t size;
 int ret = 0;

5、页面的读入

page_cache_read--->>mapping->a_ops->readpage(file, page)

这里调用的同样是上面在创建一个inode节点的时候注册的ext2_aops函数的read_page

ext2_readpage--->>mpage_readpage----->>>do_mpage_readpage

最终将会调用驱动进行真正磁盘文件的读入操作。

三、总结

系统文件的所有共享都是通过

系统唯一的文件路径==>>

系统唯一inode===>>

相同的address_space===>>>

相同的address_space中有相同的页面空间  

 这一事实,包括mmap和更为常见的同一文件创建多个任务时代码段的共享,均是基于这个原理实现。这个原理可能没什么疑问,而且也不是很复杂,但是扩展到busybox中使用该机制把整个文件系统降低为1.3M,windows下的DLL、Linux的SO就知道这个机制的强大了。事实上,很多功能单独看来都差不多,必须结合一定的场景才能看出它的能量。这一点同样和人一样,没有在自己合适的舞台上,谁都无法完全显示自己的力量和能量

  评论这张
 
阅读(2115)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017