在“分叉”一个进程时,为啥 Linux 内核会为每个新创建的进程复制内核页表的内容?
Posted
技术标签:
【中文标题】在“分叉”一个进程时,为啥 Linux 内核会为每个新创建的进程复制内核页表的内容?【英文标题】:While "fork"ing a process, why does Linux kernel copy the content of kernel page table for every newly created process?在“分叉”一个进程时,为什么 Linux 内核会为每个新创建的进程复制内核页表的内容? 【发布时间】:2015-01-29 02:01:54 【问题描述】:以下讨论适用于 32 位 ARM Linux 内核。
我注意到在fork过程中,Linux内核将内核页表(master page table,即swapper_pg_dir)的内容复制到每个新创建的进程的页表中。
问题是:
为什么要这么做呢? 为什么所有进程不能共享一个内核页面副本 表(关于 32 位 ARM Linux 的更高 1G 部分),而不是 memcpy 每个新创建的进程的交换页表? 是不是很浪费内存?相关源码(“-->”代表函数调用): do_fork --> copy_process --> copy_mm --> dup_mm --> mm_init --> mm_alloc_pgd --> pgd_alloc -->
/*
* Copy over the kernel and IO PGD entries
*/
init_pgd = pgd_offset_k(0);
memcpy(new_pgd + USER_PTRS_PER_PGD, init_pgd + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
【问题讨论】:
只有一个物理MMU;它是硬件。每个进程都需要一个完整的 1 级表 (16K)。对于内核,大多数是节/超节,大小为 1MB,没有 L2。进程可以共享 L2 条目。 感谢您的评论。你的意思是说因为只有一个物理MMU,如果内核母版页表(swapper page table)只有一个副本,那么每次内核/用户模式切换时都需要刷新MMU? 不,每个进程可能有一个连续的 16k 单独的 L1 表。切换表底座时,必须刷新所有 L1-TLB。但是,我认为 Linux 只更新 real L1 页表,并为每次更新进行 TLB 刷新;在 ARM 上有一些假的页表。抱歉,我没有看什么是 swapper_pg_dir;真实表或伪表。 Linux 有 arch 依赖/独立代码。 所以我的理解是如果 -所有进程共享相同的内核母版页表的单个副本以用于较高的 1GB 部分(内核土地)-所有进程对较低的 3GB 部分使用单独的页表(用户空间) 这意味着进程没有连续的 16K L1 表,因此每次内核-用户切换时都需要切换页表。 请您确认一下吗?谢谢! 您好,从源码看,内核页表没有完全复制。实际上只拷贝了page全局目录,也就是说进程页表和内核页表指向同一个pud、pmd和pte。 【参考方案1】:每个进程都有自己的内核部分页表副本(更高的 1GB)是为了在切换用户/内核域时避免 L1 页表切换(即避免更新 TTBR)。请注意,用户/内核域切换非常频繁。
为什么要避免更新 TTBR?详细信息可以在这里找到: What is the downside of updating ARM TTBR(Translate Table Base Register)?
【讨论】:
【参考方案2】:共享页表意味着共享内存空间。换句话说,它破坏了拥有操作系统的意义。每个进程都有自己的页面故事。页表不占用太多内存。
【讨论】:
感谢您的回答。我的观点是,为什么要将母版页表的“内容”复制到每个新创建的进程的页表中?为什么每个进程不能共享同一个内核页表(高1G部分)以上是关于在“分叉”一个进程时,为啥 Linux 内核会为每个新创建的进程复制内核页表的内容?的主要内容,如果未能解决你的问题,请参考以下文章