Linux进程间通信内存映射(mmap)实现篇

Linux 内核中 mmap 的实现(基于 2.6.12)

概述

基于 2.6.12 内核, 说明 mmap 系统调用的核心数据结构、系统调用路径及关键实现. 主要文件: mm/mmap.cmm/msync.cmm/filemap.cinclude/linux/mm.hinclude/linux/mman.h.

核心数据结构

mm_struct (进程地址空间描述符)

// include/linux/mm_types.h
struct mm_struct {
    struct vm_area_struct *mmap;        // VMA 链表头
    struct rb_root mm_rb;               // VMA 红黑树根节点
    struct vm_area_struct *mmap_cache;   // 最近使用的 VMA 缓存
    unsigned long free_area_cache;       // 空闲区域搜索的起始地址
    unsigned long mmap_base;             // mmap 区域的基地址
    unsigned long total_vm;               // 总虚拟内存页数
    unsigned long locked_vm;              // 锁定的内存页数
    struct rw_semaphore mmap_sem;        // 保护地址空间的读写信号量
    // ... 其他字段
};

vm_area_struct (虚拟内存区域)

// include/linux/mm_types.h
struct vm_area_struct {
    struct mm_struct *vm_mm;             // 所属的地址空间
    unsigned long vm_start;              // 虚拟地址区间起始地址
    unsigned long vm_end;                // 虚拟地址区间结束地址(不包含)
    struct vm_area_struct *vm_next;       // 链表中的下一个 VMA
    struct rb_node vm_rb;                // 红黑树节点
    unsigned long vm_flags;              // 权限与属性标志
    pgprot_t vm_page_prot;               // 页保护属性
    pgoff_t vm_pgoff;                    // 文件页偏移(文件映射时使用)
    struct file *vm_file;                // 关联的文件对象(文件映射时使用)
    void *vm_private_data;               // 私有数据
    struct vm_operations_struct *vm_ops;  // VMA 操作函数指针
    // ... 其他字段
};

file (文件对象)

// include/linux/fs.h
struct file {
    struct dentry *f_dentry;             // 目录项
    struct vfsmount *f_vfsmnt;           // 文件系统挂载点
    struct file_operations *f_op;         // 文件操作函数指针
    struct address_space *f_mapping;      // 地址空间(用于页缓存)
    fmode_t f_mode;                      // 文件打开模式
    // ... 其他字段
};

系统调用路径(x86_64 类推)

  • sys_mmap/sys_mmap2 (架构层入口) → do_mmap_pgoff (核心实现)
  • sys_munmapdo_munmap (解除映射)
  • sys_msyncmsync_interval (同步映射区域到文件)
  • sys_mprotectdo_mprotect (修改保护属性)

核心流程

创建映射(mmap)

流程概述:

  1. 参数校验与页对齐: 检查长度、偏移、权限标志
  2. 选择/验证地址: 非 MAP_FIXED 由 get_unmapped_area 选择合适空洞, MAP_FIXED 必须满足对齐且不与现有 VMA 冲突
  3. 权限检查与文件映射: 共享映射需要写权限匹配, 文件映射调用 file->f_op->mmap 建立关联
  4. 建立 VMA: 分配 vm_area_struct, 设置 vm_flagsvm_pgoffvm_file, 插入 mm_struct 的 VMA 红黑树和链表
  5. 返回映射起始地址
sys_mmap2 系统调用入口
// arch/x86_64/kernel/sys_x86_64.c (简化示意)
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
                          unsigned long prot, unsigned long flags,
                          unsigned long fd, unsigned long pgoff)
{
    struct file *file = NULL;
    unsigned long error;

    // 如果不是匿名映射, 获取文件指针
    if (!(flags & MAP_ANONYMOUS)) {
        file = fget(fd);
        if (!file)
            return -EBADF;
    }

    // 获取写锁保护地址空间操作
    down_write(&current->mm->mmap_sem);
    // do_mmap_pgoff 完成核心逻辑
    error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
    up_write(&current->mm->mmap_sem);

    if (file)
        fput(file);
    return error;
}
do_mmap_pgoff 核心实现
// mm/mmap.c
unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
                            unsigned long len, unsigned long prot,
                            unsigned long flags, unsigned long pgoff)
{
    struct mm_struct *mm = current->mm;
    struct vm_area_struct *vma, *prev;
    struct inode *inode;
    unsigned int vm_flags;
    int correct_wcount = 0;
    int error;
    struct rb_node **rb_link, *rb_parent;
    int accountable = 1;
    unsigned long charged = 0, reqprot = prot;

    // 1. 文件映射的初步检查
    if (file) {
        // 检查文件是否支持 mmap 操作
        if (!file->f_op || !file->f_op->mmap)
            return -ENODEV;
        // 检查执行权限与文件系统挂载标志
        if ((prot & PROT_EXEC) &&
            (file->f_vfsmnt->mnt_flags & MNT_NOEXEC))
            return -EPERM;
    }

    // 2. 处理 READ_IMPLIES_EXEC 个性标志
    // 某些架构/程序期望 PROT_READ 隐含 PROT_EXEC
    if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
        if (!(file && (file->f_vfsmnt->mnt_flags & MNT_NOEXEC)))
            prot |= PROT_EXEC;

    // 3. 参数校验: 长度不能为 0
    if (!len)
        return -EINVAL;

    // 4. 长度页对齐并检查溢出
    len = PAGE_ALIGN(len);
    if (!len || len > TASK_SIZE)
        return -ENOMEM;

    // 5. 检查文件偏移溢出
    if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)
        return -EOVERFLOW;

    // 6. 检查 VMA 数量限制
    if (mm->map_count > sysctl_max_map_count)
        return -ENOMEM;

    // 7. 获取或选择映射地址
    // 非 MAP_FIXED 时由 get_unmapped_area 选择合适地址
    addr = get_unmapped_area(file, addr, len, pgoff, flags);
    if (addr & ~PAGE_MASK)
        return addr;  // 返回错误码

    // 8. 计算 VMA 标志位
    // 将用户空间的 prot 和 flags 转换为内核的 vm_flags
    vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |
               mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;

    // 9. 处理 MAP_LOCKED 标志(锁定内存)
    if (flags & MAP_LOCKED) {
        if (!can_do_mlock())
            return -EPERM;
        vm_flags |= VM_LOCKED;
        // 检查内存锁定限制
        unsigned long locked, lock_limit;
        locked = len >> PAGE_SHIFT;
        locked += mm->locked_vm;
        lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
        lock_limit >>= PAGE_SHIFT;
        if (locked > lock_limit && !capable(CAP_IPC_LOCK))
            return -EAGAIN;
    }

    inode = file ? file->f_dentry->d_inode : NULL;

    // 10. 根据映射类型设置 VMA 标志
    if (file) {
        switch (flags & MAP_TYPE) {
        case MAP_SHARED:
            // 共享映射: 需要写权限时检查文件是否可写
            if ((prot & PROT_WRITE) && !(file->f_mode & FMODE_WRITE))
                return -EACCES;
            // 不能对追加模式文件进行共享写映射
            if (IS_APPEND(inode) && (file->f_mode & FMODE_WRITE))
                return -EACCES;
            // 检查是否有强制锁
            if (locks_verify_locked(inode))
                return -EAGAIN;
            vm_flags |= VM_SHARED | VM_MAYSHARE;
            // 如果文件不可写, 清除写权限标志
            if (!(file->f_mode & FMODE_WRITE))
                vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
            break;
        case MAP_PRIVATE:
            // 私有映射: 至少需要读权限
            if (!(file->f_mode & FMODE_READ))
                return -EACCES;
            break;
        default:
            return -EINVAL;
        }
    } else {
        // 匿名映射
        switch (flags & MAP_TYPE) {
        case MAP_SHARED:
            vm_flags |= VM_SHARED | VM_MAYSHARE;
            break;
        case MAP_PRIVATE:
            // 设置 pgoff 为虚拟地址对应的页号
            pgoff = addr >> PAGE_SHIFT;
            break;
        default:
            return -EINVAL;
        }
    }

    // 11. LSM (Linux Security Module) 安全检查
    error = security_file_mmap(file, reqprot, prot, flags);
    if (error)
        return error;

    // 12. 清除可能冲突的旧映射
    error = -ENOMEM;
munmap_back:
    vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);
    if (vma && vma->vm_start < addr + len) {
        // 如果新映射与现有 VMA 冲突, 先解除旧映射
        if (do_munmap(mm, addr, len))
            return -ENOMEM;
        goto munmap_back;
    }

    // 13. 检查地址空间限制
    if (!may_expand_vm(mm, len >> PAGE_SHIFT))
        return -ENOMEM;

    // 14. 内存记账(accounting)
    if (accountable && (!(flags & MAP_NORESERVE) ||
                        sysctl_overcommit_memory == OVERCOMMIT_NEVER)) {
        if (vm_flags & VM_SHARED) {
            // 共享映射需要记账
            vm_flags |= VM_ACCOUNT;
        } else if (vm_flags & VM_WRITE) {
            // 私有可写映射需要预留内存
            charged = len >> PAGE_SHIFT;
            if (security_vm_enough_memory(charged))
                return -ENOMEM;
            vm_flags |= VM_ACCOUNT;
        }
    }

    // 15. 尝试合并相邻的匿名私有映射
    if (!file && !(vm_flags & VM_SHARED) &&
        vma_merge(mm, prev, addr, addr + len, vm_flags,
                  NULL, NULL, pgoff, NULL))
        goto out;

    // 16. 分配 VMA 结构体
    vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
    if (!vma) {
        error = -ENOMEM;
        goto unacct_error;
    }
    memset(vma, 0, sizeof(*vma));

    // 17. 初始化 VMA 基本字段
    vma->vm_mm = mm;
    vma->vm_start = addr;
    vma->vm_end = addr + len;
    vma->vm_flags = vm_flags;
    vma->vm_page_prot = protection_map[vm_flags & 0x0f];
    vma->vm_pgoff = pgoff;

    // 18. 处理文件映射或匿名共享映射
    if (file) {
        // 处理 MAP_DENYWRITE 标志(禁止其他进程写入文件)
        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);  // 增加文件引用计数
        // 调用文件系统的 mmap 方法(通常是 generic_file_mmap)
        error = file->f_op->mmap(file, vma);
        if (error)
            goto unmap_and_free_vma;
    } else if (vm_flags & VM_SHARED) {
        // 匿名共享映射: 使用 shmem (共享内存文件系统)
        error = shmem_zero_setup(vma);
        if (error)
            goto free_vma;
    }

    // 19. 清理共享映射的 VM_ACCOUNT 标志(由 shmem 负责记账)
    if ((vm_flags & (VM_SHARED|VM_ACCOUNT)) == (VM_SHARED|VM_ACCOUNT))
        vma->vm_flags &= ~VM_ACCOUNT;

    // 20. 保存可能被文件系统 mmap 方法修改的地址和偏移
    addr = vma->vm_start;
    pgoff = vma->vm_pgoff;
    vm_flags = vma->vm_flags;

    // 21. 尝试与相邻 VMA 合并, 或插入新的 VMA
    if (!file || !vma_merge(mm, prev, addr, vma->vm_end,
                            vma->vm_flags, NULL, file, pgoff, vma_policy(vma))) {
        // 不能合并, 插入新的 VMA
        file = vma->vm_file;
        vma_link(mm, vma, prev, rb_link, rb_parent);
        if (correct_wcount)
            atomic_inc(&inode->i_writecount);
    } else {
        // 可以合并, 释放刚分配的 VMA
        if (file) {
            if (correct_wcount)
                atomic_inc(&inode->i_writecount);
            fput(file);
        }
        mpol_free(vma_policy(vma));
        kmem_cache_free(vm_area_cachep, vma);
    }

out:
    // 22. 更新统计信息
    mm->total_vm += len >> PAGE_SHIFT;
    __vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
    if (vm_flags & VM_LOCKED) {
        mm->locked_vm += len >> PAGE_SHIFT;
        // MAP_LOCKED 时立即分配物理页
        make_pages_present(addr, addr + len);
    }
    // 23. 处理 MAP_POPULATE 标志(预分配页)
    if (flags & MAP_POPULATE) {
        up_write(&mm->mmap_sem);
        sys_remap_file_pages(addr, len, 0, pgoff, flags & MAP_NONBLOCK);
        down_write(&mm->mmap_sem);
    }
    return addr;

unmap_and_free_vma:
    // 错误处理: 文件系统 mmap 失败
    if (correct_wcount)
        atomic_inc(&inode->i_writecount);
    vma->vm_file = NULL;
    fput(file);
    unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
    charged = 0;
free_vma:
    kmem_cache_free(vm_area_cachep, vma);
unacct_error:
    if (charged)
        vm_unacct_memory(charged);
    return error;
}
get_unmapped_area 地址选择
// mm/mmap.c
unsigned long get_unmapped_area(struct file *file, unsigned long addr,
                                unsigned long len, unsigned long pgoff,
                                unsigned long flags)
{
    unsigned long ret;

    // 非 MAP_FIXED 时选择地址
    if (!(flags & MAP_FIXED)) {
        unsigned long (*get_area)(struct file *, unsigned long,
                                  unsigned long, unsigned long, unsigned long);
        // 优先使用文件系统提供的地址选择函数
        get_area = current->mm->get_unmapped_area;
        if (file && file->f_op && file->f_op->get_unmapped_area)
            get_area = file->f_op->get_unmapped_area;
        addr = get_area(file, addr, len, pgoff, flags);
        if (IS_ERR_VALUE(addr))
            return addr;
    }

    // 检查地址范围是否有效
    if (addr > TASK_SIZE - len)
        return -ENOMEM;
    // 检查地址是否页对齐
    if (addr & ~PAGE_MASK)
        return -EINVAL;
    // 处理大页映射的特殊检查
    if (file && is_file_hugepages(file)) {
        ret = prepare_hugepage_range(addr, len);
    } else {
        ret = is_hugepage_only_range(current->mm, addr, len);
    }
    if (ret)
        return -EINVAL;
    return addr;
}

// 默认的地址选择算法(自底向上)
unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
                                     unsigned long len, unsigned long pgoff,
                                     unsigned long flags)
{
    struct mm_struct *mm = current->mm;
    struct vm_area_struct *vma;
    unsigned long start_addr;

    if (len > TASK_SIZE)
        return -ENOMEM;

    // 如果指定了建议地址, 先检查是否可用
    if (addr) {
        addr = PAGE_ALIGN(addr);
        vma = find_vma(mm, addr);
        if (TASK_SIZE - len >= addr &&
            (!vma || addr + len <= vma->vm_start))
            return addr;
    }
    // 从上次搜索的缓存地址开始
    start_addr = addr = mm->free_area_cache;

full_search:
    // 遍历 VMA 链表查找空闲区域
    for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
        // 检查是否超出地址空间
        if (TASK_SIZE - len < addr) {
            // 从头开始重新搜索
            if (start_addr != TASK_UNMAPPED_BASE) {
                start_addr = addr = TASK_UNMAPPED_BASE;
                goto full_search;
            }
            return -ENOMEM;
        }
        // 找到合适的空洞
        if (!vma || addr + len <= vma->vm_start) {
            // 更新缓存地址
            mm->free_area_cache = addr + len;
            return addr;
        }
        addr = vma->vm_end;
    }
}
generic_file_mmap 文件映射设置
// mm/filemap.c
int generic_file_mmap(struct file *file, struct vm_area_struct *vma)
{
    struct address_space *mapping = file->f_mapping;

    // 检查地址空间是否支持 readpage 操作(用于缺页处理)
    if (!mapping->a_ops->readpage)
        return -ENOEXEC;
    // 更新文件的访问时间
    file_accessed(file);
    // 设置 VMA 的操作函数指针(包含 fault、populate 等)
    vma->vm_ops = &generic_file_vm_ops;
    return 0;
}
vma_link 插入 VMA
// mm/mmap.c
static void vma_link(struct mm_struct *mm, struct vm_area_struct *vma,
                     struct vm_area_struct *prev, struct rb_node **rb_link,
                     struct rb_node *rb_parent)
{
    // 插入到 VMA 链表
    __vma_link_list(mm, vma, prev);
    // 插入到 VMA 红黑树
    __vma_link_rb(mm, vma, rb_link, rb_parent);
    // 如果文件映射, 还需要插入到文件的地址空间树
    if (vma->vm_file) {
        struct address_space *mapping = vma->vm_file->f_mapping;
        spin_lock(&mapping->i_mmap_lock);
        __vma_link_file(vma);
        spin_unlock(&mapping->i_mmap_lock);
    }
    // 更新 VMA 计数
    mm->map_count++;
}

解除映射(munmap)

流程概述:

  1. 对齐地址与长度: 地址必须页对齐, 长度页对齐
  2. 查找覆盖的 VMA: 找到与解除区域重叠的所有 VMA
  3. 拆分 VMA: 如果解除区域只覆盖 VMA 的一部分, 需要拆分
  4. 移除 VMA: 从链表、红黑树、文件地址空间树中移除
  5. 释放页表和资源: 调用 unmap_region 释放页表项和物理页
sys_munmap 系统调用入口
// mm/mmap.c
asmlinkage long sys_munmap(unsigned long addr, size_t len)
{
    int ret;
    struct mm_struct *mm = current->mm;

    profile_munmap(addr);

    // 获取写锁保护地址空间操作
    down_write(&mm->mmap_sem);
    ret = do_munmap(mm, addr, len);
    up_write(&mm->mmap_sem);
    return ret;
}
do_munmap 核心实现
// mm/mmap.c
int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
{
    unsigned long end;
    struct vm_area_struct *vma, *prev, *last;

    // 1. 参数校验: 地址必须页对齐, 范围必须有效
    if ((start & ~PAGE_MASK) || start > TASK_SIZE || len > TASK_SIZE - start)
        return -EINVAL;

    // 2. 长度页对齐
    if ((len = PAGE_ALIGN(len)) == 0)
        return -EINVAL;

    // 3. 查找第一个重叠的 VMA
    vma = find_vma_prev(mm, start, &prev);
    if (!vma)
        return 0;  // 没有重叠的 VMA, 直接返回成功
    // 此时 start < vma->vm_end

    // 4. 检查是否真的重叠
    end = start + len;
    if (vma->vm_start >= end)
        return 0;  // 不重叠

    // 5. 如果解除区域不是从 VMA 起始处开始, 需要拆分 VMA
    if (start > vma->vm_start) {
        int error = split_vma(mm, vma, start, 0);
        if (error)
            return error;
        prev = vma;  // 拆分后 prev 指向前半部分
    }

    // 6. 检查是否需要拆分最后一个 VMA
    last = find_vma(mm, end);
    if (last && end > last->vm_start) {
        int error = split_vma(mm, last, end, 1);
        if (error)
            return error;
    }
    // 重新定位到要解除的第一个 VMA
    vma = prev ? prev->vm_next : mm->mmap;

    // 7. 从链表和红黑树中分离要解除的 VMA
    detach_vmas_to_be_unmapped(mm, vma, prev, end);

    // 8. 解除页表映射并释放物理页
    unmap_region(mm, vma, prev, start, end);

    // 9. 释放 VMA 结构体和相关资源
    unmap_vma_list(mm, vma);

    return 0;
}

同步映射区域(msync)

流程概述:

  1. 参数校验: 地址页对齐, 标志位检查
  2. 查找覆盖的 VMA: 遍历所有与同步区域重叠的 VMA
  3. 同步脏页: 对于共享映射, 将脏页写回文件
  4. 文件系统同步: MS_SYNC 时调用文件系统的 fsync 方法
sys_msync 系统调用入口
// mm/msync.c
asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
{
    unsigned long end;
    struct vm_area_struct *vma;
    int unmapped_error, error = -EINVAL;

    // 1. 设置同步写标志(用于 I/O 调度)
    if (flags & MS_SYNC)
        current->flags |= PF_SYNCWRITE;

    down_read(&current->mm->mmap_sem);

    // 2. 参数校验: 标志位检查
    if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
        goto out;
    // 地址必须页对齐
    if (start & ~PAGE_MASK)
        goto out;
    // MS_ASYNC 和 MS_SYNC 不能同时设置
    if ((flags & MS_ASYNC) && (flags & MS_SYNC))
        goto out;

    // 3. 长度页对齐并检查溢出
    error = -ENOMEM;
    len = (len + ~PAGE_MASK) & PAGE_MASK;
    end = start + len;
    if (end < start)
        goto out;
    error = 0;
    if (end == start)
        goto out;

    // 4. 查找第一个覆盖的 VMA
    vma = find_vma(current->mm, start);
    unmapped_error = 0;

    // 5. 遍历所有覆盖的 VMA 进行同步
    for (;;) {
        error = -ENOMEM;
        if (!vma)
            goto out;
        // 处理未映射的区域
        if (start < vma->vm_start) {
            unmapped_error = -ENOMEM;
            start = vma->vm_start;
        }
        // 同步当前 VMA 覆盖的部分
        if (end <= vma->vm_end) {
            if (start < end) {
                error = msync_interval(vma, start, end, flags);
                if (error)
                    goto out;
            }
            error = unmapped_error;
            goto out;
        }
        // 同步到当前 VMA 的结束
        error = msync_interval(vma, start, vma->vm_end, flags);
        if (error)
            goto out;
        start = vma->vm_end;
        vma = vma->vm_next;
    }

out:
    up_read(&current->mm->mmap_sem);
    current->flags &= ~PF_SYNCWRITE;
    return error;
}
msync_interval 同步区间
// mm/msync.c
static int msync_interval(struct vm_area_struct *vma,
                          unsigned long addr, unsigned long end, int flags)
{
    int ret = 0;
    struct file *file = vma->vm_file;

    // 1. MS_INVALIDATE 与 VM_LOCKED 冲突
    if ((flags & MS_INVALIDATE) && (vma->vm_flags & VM_LOCKED))
        return -EBUSY;

    // 2. 只处理共享文件映射
    if (file && (vma->vm_flags & VM_SHARED)) {
        // 同步页表中的脏页到页缓存
        filemap_sync(vma, addr, end);

        // 3. MS_SYNC 时同步到磁盘
        if (flags & MS_SYNC) {
            struct address_space *mapping = file->f_mapping;
            int err;

            // 将页缓存中的脏页写回磁盘
            ret = filemap_fdatawrite(mapping);
            // 调用文件系统的 fsync 方法
            if (file->f_op && file->f_op->fsync) {
                err = file->f_op->fsync(file, file->f_dentry, 1);
                if (err && !ret)
                    ret = err;
            }
            // 等待写操作完成
            err = filemap_fdatawait(mapping);
            if (!ret)
                ret = err;
        }
    }
    return ret;
}

保护属性变更(mprotect)

流程概述:

  1. 对齐区间: 地址和长度页对齐
  2. 遍历相关 VMA: 找到所有需要修改的 VMA
  3. 校验新权限: 检查新权限是否合法(如文件不可写时不能设置写权限)
  4. 拆分 VMA: 如果只修改部分 VMA, 需要拆分
  5. 更新 vm_flags: 修改 VMA 标志位和页保护属性
  6. 刷新 TLB: 使页表缓存失效

关键限制(2.6.12 默认)

  • vm.max_map_count: 每个进程最大 VMA 数量(/proc/sys/vm/max_map_count)
  • RLIMIT_AS: 进程虚拟地址空间限制
  • RLIMIT_MEMLOCK: 进程可锁定内存限制
  • TASK_SIZE: 用户空间地址空间大小(架构相关)

等待与唤醒

  • mmap 本身不涉及等待队列, 但缺页时会在 handle_mm_fault 中可能因 I/O 阻塞
  • MAP_LOCKED/SHM_LOCK 等锁定内存场景, 会受内存限额和锁定限制影响
  • msync 的 MS_SYNC 模式会等待 I/O 完成

文件与路径

  • mm/mmap.c: mmap/munmap 系统调用实现、VMA 管理
  • mm/msync.c: msync 系统调用实现
  • mm/mprotect.c: mprotect 系统调用实现
  • mm/filemap.c: 文件映射相关操作(generic_file_mmap 等)
  • include/linux/mm.h: 内存管理相关结构体和函数声明
  • include/linux/mman.h: mmap 相关常量定义

小结

  • mmap 的核心在于 do_mmap_pgoff: 地址选择、VMA 构建、文件/匿名映射、插入 VMA 结构
  • munmap 通过 do_munmap 实现: 查找重叠 VMA、拆分、移除、释放页表
  • msync 通过 msync_interval 实现: 同步共享映射的脏页到文件
  • 2.6.12 不支持后续特性(如 memfd, userfaultfd, MAP_POPULATE 扩展行为等), 描述保持 2.6.12 视角
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值