Linux sys_mmap vm_mmap_pgoff与get_unmapped_area

📅 2026/7/21 6:40:01
Linux sys_mmap vm_mmap_pgoff与get_unmapped_area
Linux sys_mmap vm_mmap_pgoff与get_unmapped_areammap(2) 系统调用在 x86_64 架构上的入口是 ksys_mmap_pgoff统一处理匿名映射和文件映射cunsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,unsigned long prot, unsigned long flags,unsigned long fd, unsigned long pgoff){struct file *file NULL;unsigned long retval -EBADF;if (!(flags MAP_ANONYMOUS)) {audit_mmap_fd(fd, flags);file fget(fd);if (!file)goto out;if (is_file_hugepages(file))len ALIGN(len, huge_page_size(hstate_file(file)));} else if (flags MAP_HUGETLB) {...}flags ~(MAP_EXECUTABLE | MAP_DENYWRITE);retval vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);out:if (file)fput(file);return retval;}对于文件映射ksys_mmap_pgoff 通过 fget 获取 struct file 并验证。对于 hugetlbfs 映射长度被对齐到 huge page 大小。vm_mmap_pgoff 是 VMM 层映射入口负责分配新的虚拟地址空间并建立页表映射cunsigned long vm_mmap_pgoff(struct file *file, unsigned long addr,unsigned long len, unsigned long prot,unsigned long flag, unsigned long pgoff){unsigned long ret;struct mm_struct *mm current-mm;if (!file) {if (mmap_is_ia32())addr round_hint_to_min(addr);}ret security_mmap_file(file, prot, flag);if (ret)return ret;ret mmap_region(file, addr, len, VM_FLAGS, pgoff);if (ret)return ret;return ret;}security_mmap_file 由 LSM 模块实现SELinux 在此基于 file 的 inode 安全上下文和进程的安全上下文进行决策如果返回非零则映射被拒绝。mmap_region 是映射建立的核心函数。它首先检查请求的地址范围是否与现有 VMA 重叠如果重叠则通过 do_munmap 先拆除旧映射cunsigned long mmap_region(struct file *file, unsigned long addr,unsigned long len, vm_flags_t vm_flags,unsigned long pgoff){struct mm_struct *mm current-mm;struct vm_area_struct *vma, *prev;int error;if (mm-map_count sysctl_max_map_count)return -ENOMEM;addr get_unmapped_area(file, addr, len, pgoff, vm_flags);if (IS_ERR_VALUE(addr))return addr;...}get_unmapped_area 负责在进程的地址空间中找到一块空闲的连续虚拟地址区域cunsigned long get_unmapped_area(struct file *file, unsigned long addr,unsigned long len, unsigned long pgoff,unsigned long flags){unsigned long (*get_area)(struct file *, unsigned long,unsigned long, unsigned long, unsigned long);get_area current-mm-get_unmapped_area;if (file) {if (file-f_op-get_unmapped_area)get_area file-f_op-get_unmapped_area;} else if (flags MAP_SHARED) {...pgoff 0;get_area shmem_get_unmapped_area;}return get_area(file, addr, len, pgoff, flags);}对于文件映射get_unmapped_area 优先使用文件系统自定义的实现。ext4 和 xfs 通过 file_operations 中的 get_unmapped_area 提供自己的算法。默认情况下使用 arch_get_unmapped_area_topdownx86_64 使用 top-down 分配策略cunsigned long arch_get_unmapped_area_topdown(struct file *filp,unsigned long addr, unsigned long len, unsigned long pgoff,unsigned long flags){struct vm_area_struct *vma;struct mm_struct *mm current-mm;unsigned long task_size STACK_TOP_MAX;if (flags MAP_FIXED)return addr;if (addr) {addr PAGE_ALIGN(addr);vma find_vma_intersection(mm, addr, addr len);if (!vma)return addr;}if (mm-mmap_base len) {addr mm-mmap_base - len;} else {addr PAGE_ALIGN(TASK_UNMAPPED_BASE);}while (true) {vma find_vma_prev(mm, addr, prev);if (!vma || addr len vm_start_gap(vma)) {if (!prev || addr vm_end_gap(prev))return addr;}addr vm_start_gap(vma) - len;if (addr mmap_min_addr)break;}if (addr mmap_min_addr)return -ENOMEM;return addr;}算法从 mm-mmap_base默认为 STACK_TOP_MAX 减去一页向下搜索每次尝试检查候选地址是否与现有 VMA 冲突直到找到足够大的空闲区间。mmap_region 完成地址分配后调用 call_mmap即 file-f_op-mmap建立文件与进程地址空间的关联。对于 ext4ext4_file_mmap 仅在 inode 上设置了 DAX 标志并且在 DAX 设备上时才需要特殊处理否则使用 generic_file_mmap 创建一个文件映射页的 VMAcint 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;return 0;}generic_file_mmap 设置 vm_ops 为 generic_file_vm_ops该操作集包含 .fault 回调 filemap_fault在缺页时通过 address_space_operations-readpage 从文件系统读取数据到 page cache。