太长了;内核到底如何能够在/proc/$PID/maps
.
考虑以下 C 程序语句
static char page1[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)))
现在,未初始化的变量在开始时为零。我的理解是,在程序启动时,内核将未初始化的变量映射到零页,并对页面进行写时复制延迟分配。很好,这样当页面错误发生时,内核可以解释未初始化部分的脏页。
现在考虑一下这个声明
static char page1[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE))) = {'c'}
这里,加载器将在程序初始化时加载 page1 的值,并将该页标记为 RW。因此,程序完成的任何写入对于内核来说都必须是不可见的,因为不会触发页面错误。
这是我为实验而编写的程序。
#define PAGE_SIZE (4*1024)
static char page1[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE))) = {'c'};
int main()
{
char c; int i; int *d;
scanf("%c", &c); // --------- tag 1
for(i = 0; i < PAGE_SIZE; i++)
{
page1[i] = c; // --------- tag 2
}
d = malloc(sizeof(int));
while(1);
return 0;
}
现在,在标签 1(代码中的注释)之前和之后,/proc/$PID/smaps
包含的部分的输出page1
粘贴在下面的表中
斯图 | 标签 1 之前 | TAG-2 之后 |
---|---|---|
尺寸: | 8 KB | 8 KB |
内核页面大小: | 4 KB | 4 KB |
MMU页面大小: | 4 KB | 4 KB |
RSS: | 8 KB | 8 KB |
附: | 8 KB | 8 KB |
共享_清理: | 0 KB | 0 KB |
共享_脏: | 0 KB | 0 KB |
私人_清洁: | 4 KB | 0 KB |
私人_肮脏: | 4 KB | 8 KB |
参考: | 8 KB | 8 KB |
匿名的: | 4 KB | 8 KB |
正如您所看到的,粗体颜色的参数发生了变化。
问题
- 内核到底是怎么知道我写了这个页面的?
- 这个匿名字段是什么以及为什么会发生变化?
任何其他详细解释所有工作的页面/博客/手册都会有所帮助。
我猜:
也许内核将页面标记为 RO,即使它是 RW,以便触发页面错误并且可以进行计数。或者也许有其他进程不断地遍历进程的页表,但这太昂贵了。
答案1
在 Linux 支持的大多数(也许是全部,我没有检查过)架构上,MMU 会跟踪哪些页面是脏的。因此内核可以在页面初始化时清除页表项中的脏位,即使内容非零,并且MMU会在页面修改时更新它。
匿名内存是没有文件支持的内存。通过写入页面,您增加了非文件支持的内存量 - 在初始化时,它由可执行映像支持,但一旦更改就不再是。
要查看内核在何处检查脏页,请查看供参考pte_dirty
。
内核还维护有点“软脏”,可以从用户空间中清除并由检查点恢复使用。