在“分叉”一个进程时,为啥 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 内核会为每个新创建的进程复制内核页表的内容?的主要内容,如果未能解决你的问题,请参考以下文章

内核支持线程 & 用户级线程

linux内核——进程管理

Linux查看进程打开多少文件描述符命令

linux为啥需要内核栈,系统调用时直接使用用户栈不行吗

为啥 group_concat 会为每一行带来所有产品 ID

linux进程为啥有用户栈和内核栈,