Chinaunix首页 | 论坛 | 博客
  • 博客访问: 435706
  • 博文数量: 99
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 1012
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-20 16:30
个人简介

linux kernel 工程师

文章分类

全部博文(99)

文章存档

2018年(5)

2017年(12)

2016年(27)

2015年(10)

2014年(43)

2012年(2)

我的朋友

分类: LINUX

2015-12-30 12:19:01

mmap_region是mmap实现的另一个关键点,它实现
对设备文件或普通文件的file->f_op->mmap(file, vma)函数的调用

unsigned long mmap_region(struct file *file, unsigned long addr,
              unsigned long len, unsigned long flags,
              vm_flags_t vm_flags, unsigned long pgoff)
{

    /* 分配一个struct vm_area_struct结构 */
    vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
...

    vma->vm_mm = mm;
    vma->vm_start = addr;
    vma->vm_end = addr + len;
    vma->vm_flags = vm_flags;
    vma->vm_page_prot = vm_get_page_prot(vm_flags);
    vma->vm_pgoff = pgoff;
    INIT_LIST_HEAD(&vma->anon_vma_chain);

    error = -EINVAL;    /* when rejecting VM_GROWSDOWN|VM_GROWSUP */

    if (file) {
        if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
            goto free_vma;
        if (vm_flags & VM_DENYWRITE) {
            error = deny_write_access(file);
            if (error)
                goto free_vma;
            correct_wcount = 1;
        }
        vma->vm_file = file;
        get_file(file);

        /* 对于file !=NULL 的情况,这里会调用到file的file_operations结构里面的mmap 方法
            因而对于一个设备,这里就会调用到设备驱动程序里面的mmap方法

            对于一个普通文件,调用对应的文件系统里面注册的file_operations结构里面的mmap
            1. 比如ext4fs, fs/ext4/file.c里面,
            const struct file_operations ext4_file_operations = {
                    ...
                    .mmap        = ext4_file_mmap,
                    ...
            };

            ext4_file_mmap也只是为vma->vm_ops 赋值 &ext4_file_vm_ops;并没有执行真正的map动作。

            static int ext4_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 = &ext4_file_vm_ops;
              vma->vm_flags |= VM_CAN_NONLINEAR;
              return 0;
            }

        2. 再比如ext3fs, fs/ext3/file.c里面
            const struct file_operations ext3_file_operations = {
                ...
            .mmap        = generic_file_mmap,
                ...
            };

            generic_file_mmap也只是为vma->vm_ops 赋值 &generic_file_vm_ops;并没有执行真正的map动作。

             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 -ENODEV;
            file_accessed(file);
            vma->vm_ops = &generic_file_vm_ops;
            vma->vm_flags |= VM_CAN_NONLINEAR;
            return 0;
            }

            当对应的虚拟内存被访问时,将触发访存异常。内核捕捉到异常,再完成内存分配和读文件的事情
            do_page_fault就是内核用于捕捉访存异常的函数。
            其中内核会先确认引起异常的内存地址是合法的,并且找出它所对应的vma(如果找不到就是不合法)。
            然后分配内存、建立页表。
            对于本文中描述的mmap映射了某个文件的这种情况,内核还需要把文件对应位置上的数据读到新分配的内存上,
            这个工作主要是由vma->vm_ops->fault来完成的。前面我们看到vma->vm_ops是如何被赋值的了,而且这个vma->vm_ops->fault就等于filemap_fault。
        */
        error = file->f_op->mmap(file, vma);
        if (error)
            goto unmap_and_free_vma;

#ifdef CONFIG_PAX_SEGMEXEC
        if (vma_m && (vm_flags & VM_EXECUTABLE))
            added_exe_file_vma(mm);
#endif

#if defined(CONFIG_PAX_PAGEEXEC) && defined(CONFIG_X86_32)
        if ((mm->pax_flags & MF_PAX_PAGEEXEC) && !(vma->vm_flags & VM_SPECIAL)) {
            vma->vm_flags |= VM_PAGEEXEC;
            vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
        }
#endif

        if (vm_flags & VM_EXECUTABLE)
            added_exe_file_vma(mm);

        /* Can addr have changed??
         *
         * Answer: Yes, several device drivers can do it in their
         *         f_op->mmap method. -DaveM
         */
        addr = vma->vm_start;
        pgoff = vma->vm_pgoff;
        vm_flags = vma->vm_flags;
    } else if (vm_flags & VM_SHARED) {
        if (unlikely(vm_flags & (VM_GROWSDOWN|VM_GROWSUP)))
            goto free_vma;
        error = shmem_zero_setup(vma);
        if (error)
            goto free_vma;
    }
......
}

阅读(2134) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~