Linux mlock锁定页面mlock_all与mlockall系统调用

发布时间:2026/9/11 7:20:14

Linux mlock锁定页面mlock_all与mlockall系统调用 mlock 与 mlockall页面锁定机制的内核实现mm/mlock.c 中 mlock 的入口点是 SYSCALL_DEFINE2(mlock)经过 do_mlock 到达核心函数 mlock_fixup。mlock_fixup 的原型为cstatic int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,unsigned long start, unsigned long end, unsigned int newflags)该函数首先检查目标区间是否跨越多个 VMA。若 start 或 end 落在 VMA 内部而非边界则调用 split_vma 将其分割确保 [start, end) 精确对应一个完整的 VMA。这是 mlock 实现中第一个边界条件的处理点split_vma 可能因 slab 分配失败而返回 -ENOMEM导致整个 mlock 调用失败且用户空间看到部分 VMA 已被分裂。内核对此不做回滚因为分裂本身没有改变内存布局的可见语义。mlock_fixup 的第二个关键步骤是调用 populate_vma_page_rangecstatic long populate_vma_page_range(struct vm_area_struct *vma,unsigned long start, unsigned long end, bool *nonblocking)该函数通过 __get_user_pages 以 FOLL_MLOCK | FOLL_FORCE | FOLL_WRITE | FOLL_POPULATE 为标志遍历 [start, end) 区间的所有虚拟地址。__get_user_pages 会触发缺页将尚未物理分配的页面从零页或文件页分配物理内存然后通过 rmap 路径调用 mlock_page。对于已经存在的页面__mlock_vma_pages_range 使用 walk_page_range 配合 mlock_pte 回调进行页表扫描cstatic void mlock_pte(struct page *page, unsigned long addr,unsigned long end, struct mm_walk *walk){if (!page || is_zone_device_page(page))return;if (page_mapcount(page) !PageUnevictable(page))mlock_vma_page(page);}这里 page_mapcount(page) 的检查隐含了一个竞争场景当两个线程同时 mlock 同一个页面时第一个线程设置 PG_unevictable 后第二个线程的 page_mapcount 条件仍然满足但 PageUnevictable 已经为真因此 mlock_vma_page 会被跳过第二次操作成为空操作。这是故意的——mlock 是幂等的。但若其中一个线程先 munlock则会出现竞态munlock 清除 PG_mlocked 并试图将页面移回可回收 LRU 链表而此时另一个线程正通过 mlock_pte 重新锁定。这个窗口受 mmap_lock 保护mlock 和 munlock 都需要持有 mmap_lock 的写锁。页面级别的 mlock 状态由两个页标志位管理PG_mlocked 和 PG_unevictable。mlock_vma_page 的调用链为cvoid mlock_vma_page(struct page *page){/* Serialize with page migration */get_page(page);lru_add_drain();local_lock(mlock_lock);if (!TestSetPageMlocked(page))__mlock_page(page, lruvec);local_unlock(mlock_lock);put_page(page);}__mlock_page 检查页面是否已在 unevictable LRU 上。若不在则调用 lru_cache_add_inactive_or_unevictable 将其置入 unevictable 链表。这里使用 local_lock(mlock_lock) 来保护同一个 page 上的并发操作而非使用全局锁——页面隔离isolate_page和 LRU 操作本身通过 lruvec-lru_lock 做更粗粒度的同步。这一层的局部锁只防范 mlock_count 的并发更新。PG_mlocked 和 PG_unevictable 的关系需要精确理解PG_mlocked 是每页的锁定标记PG_unevictable 是 LRU 链表收容标记。当页面被 migrate 时migrate_page_move_mapping 会转移 PG_mlocked而新页面的 PG_unevictable 由迁移代码的 lru_cache_add 路径重新建立。这发生在 __unmap_and_move 中cstatic int __unmap_and_move(struct page *page, struct page *newpage,int force, enum migrate_mode mode){/* ... */if (PageMlocked(page)) {/* Holding the page locked ensures PG_mlocked cannot change */SetPageMlocked(newpage);ClearPageMlocked(page);}/* ... */}PageMlocked 的检查在 page lock 下进行——这是防止 PG_mlocked 在迁移期间被 concurrent munlock 修改的关键设计。同样mlock_vma_page 中的 get_page/put_page 确保页面在操作期间不会被释放。mlockall 的实现通过 apply_mlockall_flagscstatic int apply_mlockall_flags(int flags){struct vm_area_struct *vma, *prev NULL;vm_flags_t to_add 0;if (flags MCL_CURRENT) {/* ... */vma_iter_init(vmi, current-mm, 0);for_each_vma(vmi, vma) {vm_flags_t newflags vma-vm_flags ~VM_LOCKED_MASK;newflags | to_add;if (!(newflags VM_LOCKED))newflags ~VM_LOCKEDONFAULT;/* ... mlock_fixup on each vma ... */}}if (flags MCL_FUTURE)current-mm-def_flags | VM_LOCKED; // or VM_LOCKEDONFAULTreturn 0;}MCL_CURRENT 遍历所有 VMA对每个 VMA 调用 mlock_fixup这意味着 VMA 数量极大的进程如大量 mmap 了文件映射的数据库会在 mlockall(MCL_CURRENT) 中产生 O(n_vmas) 次 split_vma 和 populate_vma_page_range 调用且每次调用都会竞争 mmap_lock 的写锁。这是 mlockall 的核心性能问题即使每个 VMA 的页面已经 residentwalk_page_range 的页表扫描也会消耗大量 CPU 时间。MCL_FUTURE 的语义很简单只需修改 current-mm-def_flags。此后所有通过 do_mmap 创建的 VMA 都会继承 VM_LOCKED。但这里有一个容易被忽略的细节def_flags 不应用于通过 brk 扩展的堆也不应用于通过 shmat 获得的共享内存段。堆的扩展在 do_brk_flags 中独立处理shmat 的 VMA 创建在 do_mmap_pgoff 路径中也绕过了 def_flags 的继承——除非显式在架构代码中做了处理。MCL_ONFAULT 是 4.4 内核引入的标志使 mlock 不立即触发缺页而是设置 VM_LOCKEDONFAULT。该标志与 VM_LOCKED 的核心区别在于 populate_vma_page_range 的调用行为VM_LOCKEDONFAULT 不会在 mlock_fixup 中调用缺页。页面只在首次访问的缺页路径中通过 __mlock_page 被锁定。这是减少 mlockall 延迟的关键优化。RLIMIT_MEMLOCK 的检查由 can_do_mlock 和 account_locked_vm 共同完成cstatic inline int can_do_mlock(void){if (rlimit(RLIMIT_MEMLOCK) ! 0)return 1;if (capable(CAP_IPC_LOCK))return 1;return 0;}int account_locked_vm(struct mm_struct *mm, unsigned long pages, bool inc){if (inc) {if (!capable(CAP_IPC_LOCK) (mm-locked_vm pages) rlimit(RLIMIT_MEMLOCK))return -ENOMEM;mm-locked_vm pages;} else {mm-locked_vm - pages;}return 0;}locked_vm 存储在 mm_struct 中是进程粒度的计数。mlock_fixup 在调用 populate_vma_page_range 之前计算新增锁定页数量通过 account_locked_vm 预校验后再实际执行缺页。若缺页过程中某些页面分配失败当前实现不会回退 locked_vm 计数导致计数偏高但实际锁定页偏少的状况。这一差异在正常系统上极少出现但在 cgroup 内存上限压力下__get_user_pages 的分配可能因 memory cgroup 限制而部分失败。在 LRU 回收路径中页面被锁定在 unevictable list 上vmscan 的 shrink_active_list 和 shrink_inactive_list 会跳过这些页面。check_move_unevictable_pages 是专门用于在 compaction 后将页面移回 unevictable list 的函数处理因页面迁移导致的 LRU 归属变化。透明大页与 mlock 的交互较为复杂。当 __split_huge_page 分裂一个 2M THP 时所有 512 个 base page 继承 head page 的 PG_mlocked 标志但 mlock 的 per-page 计数需要同时调整。在 split_huge_page_to_list 中cvoid split_huge_page_to_list(struct page *page, struct list_head *list){/* ... */for (i 0; i HPAGE_PMD_NR; i) {if (PageMlocked(head))SetPageMlocked(page i);/* ... */}/* ... */}但如果 head page 只有一个 mlock 计数mlock_count分裂后 512 个 page 各自持有 PG_mlocked后续 munlock 时每个子页面都需要单独清除标志并尝试从 unevictable LRU 移除。这是 THP 分裂场景下 munlock 性能显著下降的原因——一次分裂将 O(1) 的 munlock 操作放大为 O(512)。mlock_count 是 struct page 中一个与 page-lru 复用的字段仅在 PG_mlocked 设置时有效。当同一个页面被多次 mlock例如父子进程 share 同一个匿名页后分别 mlockmlock_count 递增munlock 时递减至零才真正清除 PG_mlocked 并解除 unevictable 状态。mlock_count 的更新受 page lock 保护cstatic inline void __mlock_page(struct page *page, struct lruvec *lruvec){int nr_pages thp_nr_pages(page);if (page_mapcount(page) !PageUnevictable(page))lru_cache_add_inactive_or_unevictable(page, lruvec);if (TestSetPageMlocked(page))page-mlock_count nr_pages;elsepage-mlock_count nr_pages;}munlock 的批量处理是性能优化的关键。__munlock_pagevec 函数累积最多 MMVEC_PAGEVEC_SIZE默认 15个待清理页面后一次性获取 lruvec-lru_lock 进行处理减少锁获取的缓存行乒乓cstatic void __munlock_pagevec(struct pagevec *pvec, struct lruvec *lruvec){int i;for (i 0; i pagevec_count(pvec); i) {struct page *page pvec-pages[i];if (TestClearPageMlocked(page)) {/* ... */if (page_count(page) 1 page_mapcount(page))/* only referenced by mapping, can be reclaimed */__munlock_isolate_lru_page(page, lruvec, pvec_mapped);elseunlock_page(page);} else {unlock_page(page);}}/* ... batch move from unevictable to active/inactive ... */}这里 page_count(page) 1 page_mapcount(page) 的判定是 munlock 的一个微妙边界条件。该等式成立表示该页面仅被其 mapping 引用mapping 的 ref 计为 1和进程页表引用mapcount没有额外的 pin 或内核引用。如果页面还被 get_user_pages 固定例如被 v4l2 或 RDMA 驱动 pin 住page_count 会更高则 __munlock_isolate_lru_page 不会将该页移出 unevictable 链表避免回收正在被 DMA 写入的页面。mlock 与 OOM killer 的关系是设计上的权衡locked 页面不会被 OOM killer 回收。当系统内存不足且大量页面被 mlock 时OOM killer 只能选择杀死持有少量 mlock 的进程以释放非锁定内存。shrink_node 中的 shrink_list 调用 isolate_lru_pages 时指定了 sc-may_unevictable 标志但 unevictable LRU 上的页面不会被正常回收扫描触及。若 mlock 占用了大部分可回收内存系统可能被迫频繁触发 direct reclaim 和 OOM而 mlock 页面本身不受影响。mlockall(MCL_CURRENT | MCL_FUTURE) 是性能敏感场景中最重的操作。它将遍历所有 VMA通过 populate_vma_page_range 触发所有映射页面的缺页并设置 future VMA 继承 VM_LOCKED。对于内存映射数据库如 MongoDB 或 Oracle DBmlockall 可能导致持续数十秒的 mmap_lock 写锁持有期间所有线程的 page fault、mmap、munmap、brk 都会阻塞。缓解手段是将 mlockall 放在进程初始化阶段、使用 MCL_ONFAULT 延迟缺页或在 NUMA 系统上先 bind 内存再 mlock 以减少 remote node 的缺页延迟。最后一个需要注意的实现细节mlock 传递到 mmap_lock 的竞争。mlock 系统调用以 write lock 持有 mmap_lock但 populate_vma_page_range 中的 __get_user_pages 在缺页路径中可能触发 disk I/O文件映射的 page cache 页面不在内存时此时 mmap_lock 被 write held 会导致整个进程阻塞在 I/O 上。内核通过 FOLL_NOWAIT 标志限制某些场景的行为但 mlock 的缺页路径默认允许阻塞。在 mmap_lock 从读写信号量迁移到 rwsem 后write lock 下的 I/O 阻塞仍可能触发 lockdep 警告但实际执行中是通过 down_write_killable 来避免死锁。
延伸阅读

更多相关文章

2026/9/11 18:38:56

Linux memory compaction内存压缩触发条件

Linux Memory Compaction 触发条件mm/compaction.c 中的 compact_zone() 和 isolate_migratepages() 是内存压缩的核心路径,但触发这些函数的入口点散布在整个内存管理子系统中。理解触发条件是诊断碎片化问题的前提。c // mm/compaction.c enum compact_result { C…

2026/9/11 10:05:51

Linux memcg_wb_domain脏页写回domain与cgroup关联

Linux memcg_wb_domain脏页写回domain与cgroup关联memcg_wb_domain是memory cgroup与writeback(回写)子系统之间的关键桥梁。在cgroup v2环境下,每个memory cgroup拥有独立的脏页写回domain,这使得内核可以基于cgroup粒度控制脏页比例、回写阈值和回写速…

2026/9/11 10:44:08

3个关键升级让魔兽争霸3在现代电脑上焕发新生

3个关键升级让魔兽争霸3在现代电脑上焕发新生 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 你是否还在为魔兽争霸3的卡顿、黑边、地图限制而烦恼&am…

2026/9/12 0:54:22

西门子S7-1200 PLC智能停车场系统开发实践

1. 项目概述作为一名工业自动化领域的工程师,我最近完成了一个基于西门子S7-1200 PLC的智能停车场车位控制系统项目。这个系统通过PLC控制实现了车位状态的实时监测、空位引导和收费管理等功能,大幅提升了停车场的运营效率。在传统停车场中,车…

2026/9/12 0:54:22

变步长LMS算法在信号去噪中的Matlab实现与参数调试

简介:面向信号处理相关课程学生与算法入门者的变步长LMS信号去噪Matlab仿真代码,用于改善经典LMS算法中固定步长带来的收敛速度与稳态误差冲突。包内共3个文件,包含1个m源文件与2张运行结果图片,压缩包整体仅63KB,轻量…

2026/9/12 0:54:22

2026软考高级科目全解析:选科策略与备考指南

1. 软考高级科目全景概览2026年的软考高级认证体系已经发展得相当成熟,包含信息系统项目管理师、系统架构设计师、系统分析师、网络规划设计师、系统规划与管理师五大核心科目。每个科目都有其独特的定位和考察侧重点,我们先从整体上把握各科目的基本特征…

2026/9/12 0:54:22

频分复用通信模型Matlab仿真:bandpass.m滤波与相干解调实现

简介:频分复用通信模型MATLAB仿真研究,面向通信工程、电子信息类学生及需要完成课程设计或毕业设计的开发者,用于理解FDM系统中多路信号在频域上如何通过不同载波进行频谱搬移、叠加合成为一路复合信号,再经接收端带通滤波与解调还…

2026/9/12 0:54:22

谷歌1800亿美元砸向AI基建:算力军备竞赛的底层逻辑与产业机会

谷歌这轮1800亿美元的资本开支预告,确实把整个科技圈和资本市场的情绪都搅动了。消息出来当天,Alphabet股价一度跌了将近两个点,盘后又拉回来,这种反复拉扯本身就说明一个问题:市场对“AI到底值不值得这么砸钱”这件事…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/12 0:04:17

MATLAB仿生优化框架:长鼻浣熊算法多策略融合实现

简介:本资源是一份面向智能优化算法研究者与MATLAB初学者的仿生智能算法实践代码包,聚焦于长鼻浣熊优化算法(COA)的多策略改进与性能验证。针对传统COA易陷局部最优、收敛精度不足等问题,作者融合Circle映射初始化提升…

2026/9/12 0:04:17

【JAVA毕设源码分享】基于 JavaWeb 的校园一卡通管理系统的设计与实现 基于 JavaWeb 的校园卡业务管理系统(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/9/12 0:04:17

【JAVA毕设源码分享】基于 Java 的图书馆借阅管理平台的搭建与实现 基于 Java 的图书馆综合管理系统(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码