From 964d43e000a74ab981b4ace903094a775001c49a Mon Sep 17 00:00:00 2001 From: Dongliang Mu Date: Fri, 3 May 2024 17:11:02 +0800 Subject: [PATCH] resolve gitbook links in MM --- MM/linux-mm-1.md | 8 ++++---- MM/linux-mm-2.md | 16 ++++++++-------- MM/linux-mm-3.md | 28 ++++++++++++++-------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/MM/linux-mm-1.md b/MM/linux-mm-1.md index 56f5509..cad9952 100644 --- a/MM/linux-mm-1.md +++ b/MM/linux-mm-1.md @@ -4,12 +4,12 @@ 简介 -------------------------------------------------------------------------------- -内存管理是操作系统内核中最复杂的部分之一(我认为没有之一)。在[讲解内核进入点之前的准备工作](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-3.html)时,我们在调用 `start_kernel` 函数前停止了讲解。`start_kernel` 函数在内核启动第一个 `init` 进程前初始化了所有的内核特性(包括那些依赖于架构的特性)。你也许还记得在引导时建立了初期页表、识别页表和固定映射页表,但是复杂的内存管理部分还没有开始工作。当 `start_kernel` 函数被调用时,我们会看到从初期内存管理到更复杂的内存管理数据结构和技术的转变。为了更好地理解内核的初始化过程,我们需要对这些技术有更清晰的理解。本章节是内存管理框架和 API 的不同部分的概述,从 `memblock` 开始。 +内存管理是操作系统内核中最复杂的部分之一(我认为没有之一)。在[讲解内核进入点之前的准备工作](/Initialization/linux-initialization-3.md)时,我们在调用 `start_kernel` 函数前停止了讲解。`start_kernel` 函数在内核启动第一个 `init` 进程前初始化了所有的内核特性(包括那些依赖于架构的特性)。你也许还记得在引导时建立了初期页表、识别页表和固定映射页表,但是复杂的内存管理部分还没有开始工作。当 `start_kernel` 函数被调用时,我们会看到从初期内存管理到更复杂的内存管理数据结构和技术的转变。为了更好地理解内核的初始化过程,我们需要对这些技术有更清晰的理解。本章节是内存管理框架和 API 的不同部分的概述,从 `memblock` 开始。 内存块 -------------------------------------------------------------------------------- -内存块是在引导初期,泛用内核内存分配器还没有开始工作时对内存区域进行管理的方法之一。以前它被称为 `逻辑内存块`,但是内核接纳了 [Yinghai Lu 提供的补丁](https://lkml.org/lkml/2010/7/13/68)后改名为 `memblock` 。`x86_64` 架构上的内核会使用这个方法。我们已经在[讲解内核进入点之前的准备工作](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-3.html)时遇到过了它。现在是时候对它更加熟悉了。我们会看到它是被怎样实现的。 +内存块是在引导初期,泛用内核内存分配器还没有开始工作时对内存区域进行管理的方法之一。以前它被称为 `逻辑内存块`,但是内核接纳了 [Yinghai Lu 提供的补丁](https://lkml.org/lkml/2010/7/13/68)后改名为 `memblock` 。`x86_64` 架构上的内核会使用这个方法。我们已经在[讲解内核进入点之前的准备工作](/Initialization/linux-initialization-3.md)时遇到过了它。现在是时候对它更加熟悉了。我们会看到它是被怎样实现的。 我们首先会学习 `memblock` 的数据结构。以下所有的数据结构都在 [include/linux/memblock.h](https://github.com/torvalds/linux/blob/master/include/linux/memblock.h) 头文件中定义。 @@ -160,7 +160,7 @@ On this step the initialization of the `memblock` structure has been finished an memblock_add_range(&memblock.memory, base, size, MAX_NUMNODES, 0); ``` -函数。我们将内存块类型 - `memory`,内存基址和内存区域大小,节点的最大数目和标志传进去。如果 `CONFIG_NODES_SHIFT` 没有被设置,最大节点数目就是 1,否则是 `1 << CONFIG_NODES_SHIFT`。`memblock_add_range` 函数将新的内存区域加到了内存块中,它首先检查传入内存区域的大小,如果是 0 就直接返回。然后,这个函数会用 `memblock_type` 来检查 `memblock` 中的内存区域是否存在。如果不存在,我们就简单地用给定的值填充一个新的 `memory_region` 然后返回(我们已经在[对内核内存管理框架的初览](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-3.html)中看到了它的实现)。如果 `memblock_type` 不为空,我们就会使用提供的 `memblock_type` 将新的内存区域加到 `memblock` 中。 +函数。我们将内存块类型 - `memory`,内存基址和内存区域大小,节点的最大数目和标志传进去。如果 `CONFIG_NODES_SHIFT` 没有被设置,最大节点数目就是 1,否则是 `1 << CONFIG_NODES_SHIFT`。`memblock_add_range` 函数将新的内存区域加到了内存块中,它首先检查传入内存区域的大小,如果是 0 就直接返回。然后,这个函数会用 `memblock_type` 来检查 `memblock` 中的内存区域是否存在。如果不存在,我们就简单地用给定的值填充一个新的 `memory_region` 然后返回(我们已经在[对内核内存管理框架的初览](/Initialization/linux-initialization-3.md)中看到了它的实现)。如果 `memblock_type` 不为空,我们就会使用提供的 `memblock_type` 将新的内存区域加到 `memblock` 中。 首先,我们获取了内存区域的结束点: @@ -415,4 +415,4 @@ memblock_dbg("memblock_reserve: [%#016llx-%#016llx] flags %#02lx %pF\n", * [e820](http://en.wikipedia.org/wiki/E820) * [numa](http://en.wikipedia.org/wiki/Non-uniform_memory_access) * [debugfs](http://en.wikipedia.org/wiki/Debugfs) -* [对内核内存管理框架的初览](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-3.html) +* [对内核内存管理框架的初览](/Initialization/linux-initialization-3.md) diff --git a/MM/linux-mm-2.md b/MM/linux-mm-2.md index cb52ebd..cab4b47 100644 --- a/MM/linux-mm-2.md +++ b/MM/linux-mm-2.md @@ -4,7 +4,7 @@ 固定映射地址和输入输出重映射 -------------------------------------------------------------------------------- -固定映射地址是一组特殊的编译时确定的地址,它们与物理地址不一定具有减 `__START_KERNEL_map` 的线性映射关系。每一个固定映射的地址都会映射到一个内存页,内核会像指针一样使用它们,但是绝不会修改它们的地址。这是这种地址的主要特点。就像注释所说的那样,“在编译期就获得一个常量地址,只有在引导阶段才会被设定上物理地址。”你在本书的[前面部分](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-1.html)可以看到,我们已经设定了 `level2_fixmap_pgt` : +固定映射地址是一组特殊的编译时确定的地址,它们与物理地址不一定具有减 `__START_KERNEL_map` 的线性映射关系。每一个固定映射的地址都会映射到一个内存页,内核会像指针一样使用它们,但是绝不会修改它们的地址。这是这种地址的主要特点。就像注释所说的那样,“在编译期就获得一个常量地址,只有在引导阶段才会被设定上物理地址。”你在本书的[前面部分](/Initialization/linux-initialization-1.md)可以看到,我们已经设定了 `level2_fixmap_pgt` : ```assembly NEXT_PAGE(level2_fixmap_pgt) @@ -79,7 +79,7 @@ static inline unsigned long virt_to_fix(const unsigned long vaddr) 一个 PFN 是一块页大小物理内存的下标。一个物理地址的 PFN 可以简单地定义为 (page_phys_addr >> PAGE_SHIFT); -`__virt_to_fix` 会清空给定地址的前 12 位,然后用固定映射区域的末地址(`FIXADDR_TOP`)减去它并右移 `PAGE_SHIFT` 即 12 位。让我们来解释它的工作原理。就像我已经写的那样,这个宏会使用 `x & PAGE_MASK` 来清空前 12 位。然后我们用 `FIXADDR_TOP` 减去它,就会得到 `FIXADDR_TOP` 的后 12 位。我们知道虚拟地址的前 12 位代表这个页的偏移量,当我们右移 `PAGE_SHIFT` 后就会得到 `Page frame number` ,即虚拟地址的所有位,包括最开始的 12 个偏移位。固定映射地址在[内核中多处使用](http://lxr.free-electrons.com/ident?i=fix_to_virt)。 `IDT` 描述符保存在这里,[英特尔可信赖执行技术](http://en.wikipedia.org/wiki/Trusted_Execution_Technology) UUID 储存在固定映射区域,以 `FIX_TBOOT_BASE` 下标开始。另外, [Xen](http://en.wikipedia.org/wiki/Xen) 引导映射等也储存在这个区域。我们已经在[内核初始化的第五部分](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-5.html)看到了一部分关于固定映射地址的知识。接下来让我们看看什么是 `ioremap`,看看它是怎样实现的,与固定映射地址又有什么关系呢? +`__virt_to_fix` 会清空给定地址的前 12 位,然后用固定映射区域的末地址(`FIXADDR_TOP`)减去它并右移 `PAGE_SHIFT` 即 12 位。让我们来解释它的工作原理。就像我已经写的那样,这个宏会使用 `x & PAGE_MASK` 来清空前 12 位。然后我们用 `FIXADDR_TOP` 减去它,就会得到 `FIXADDR_TOP` 的后 12 位。我们知道虚拟地址的前 12 位代表这个页的偏移量,当我们右移 `PAGE_SHIFT` 后就会得到 `Page frame number` ,即虚拟地址的所有位,包括最开始的 12 个偏移位。固定映射地址在[内核中多处使用](http://lxr.free-electrons.com/ident?i=fix_to_virt)。 `IDT` 描述符保存在这里,[英特尔可信赖执行技术](http://en.wikipedia.org/wiki/Trusted_Execution_Technology) UUID 储存在固定映射区域,以 `FIX_TBOOT_BASE` 下标开始。另外, [Xen](http://en.wikipedia.org/wiki/Xen) 引导映射等也储存在这个区域。我们已经在[内核初始化的第五部分](/Initialization/linux-initialization-5.md)看到了一部分关于固定映射地址的知识。接下来让我们看看什么是 `ioremap`,看看它是怎样实现的,与固定映射地址又有什么关系呢? 输入输出重映射 -------------------------------------------------------------------------------- @@ -133,7 +133,7 @@ $ cat /proc/ioports * `n` - 区域的长度; * `name` - 区域需求者的名字。 -`request_region` 分配 I/O 端口区域。通常在 `request_region` 之前会调用 `check_region` 来检查传入的地址区间是否可用,然后 `release_region` 会释放这个内存区域。`request_region` 返回指向 `resource` 结构体的指针。 `resource` 结构体是对系统资源的树状子集的抽象。我们已经在[内核初始化的第五部分](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-5.html)见到过它了,它的定义是这样的: +`request_region` 分配 I/O 端口区域。通常在 `request_region` 之前会调用 `check_region` 来检查传入的地址区间是否可用,然后 `release_region` 会释放这个内存区域。`request_region` 返回指向 `resource` 结构体的指针。 `resource` 结构体是对系统资源的树状子集的抽象。我们已经在[内核初始化的第五部分](/Initialization/linux-initialization-5.md)见到过它了,它的定义是这样的: ```C struct resource { @@ -258,13 +258,13 @@ static inline const char *e820_type_to_string(int e820_type) 我们可以在 `/proc/iomem` 中看到它们。 -现在让我们尝试着理解 `ioremap` 是如何工作的。我们已经了解了一部分 `ioremap` 的知识,我们在[内核初始化的第五部分](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-5.html)见过它。如果你读了那个章节,你就会记得 [arch/x86/mm/ioremap.c](https://github.com/torvalds/linux/blob/master/arch/x86/mm/ioremap.c) 文件中对 `early_ioremap_init` 函数的调用。对 `ioremap` 的初始化分为两个部分:有一部分在我们正常使用 `ioremap` 之前,但是要首先进行 `vmalloc` 的初始化并调用 `paging_init` 才能进行正常的 `ioremap` 调用。我们现在还不了解 `vmalloc` 的知识,先看看第一部分的初始化。首先 `early_ioremap_init` 会检查固定映射是否与页中部目录对齐: +现在让我们尝试着理解 `ioremap` 是如何工作的。我们已经了解了一部分 `ioremap` 的知识,我们在[内核初始化的第五部分](/Initialization/linux-initialization-5.md)见过它。如果你读了那个章节,你就会记得 [arch/x86/mm/ioremap.c](https://github.com/torvalds/linux/blob/master/arch/x86/mm/ioremap.c) 文件中对 `early_ioremap_init` 函数的调用。对 `ioremap` 的初始化分为两个部分:有一部分在我们正常使用 `ioremap` 之前,但是要首先进行 `vmalloc` 的初始化并调用 `paging_init` 才能进行正常的 `ioremap` 调用。我们现在还不了解 `vmalloc` 的知识,先看看第一部分的初始化。首先 `early_ioremap_init` 会检查固定映射是否与页中部目录对齐: ```C BUILD_BUG_ON((fix_to_virt(0) + PAGE_SIZE) & ((1 << PMD_SHIFT) - 1)); ``` -更多关于 `BUILD_BUG_ON` 的内容你可以在[内核初始化的第一部分](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-1.html)看到。如果给定的表达式为真,`BUILD_BUG_ON` 宏就会抛出一个编译时错误。在检查后的下一步,我们可以看到对 `early_ioremap_setup` 函数的调用,这个函数定义在 [mm/early_ioremap.c](https://github.com/torvalds/linux/blob/master/mm/early_ioremap.c) 文件中。这个函数代表了对 `ioremap` 的大体初始化。`early_ioremap_setup` 函数用初期固定映射的地址填充了 `slot_virt` 数组。所有初期固定映射地址在内存中都在 `__end_of_permanent_fixed_addresses` 后面,它们从 `FIX_BITMAP_BEGIN` 开始,到 `FIX_BITMAP_END` 结束。实际上初期 `ioremap` 会使用 `512` 个临时引导时映射: +更多关于 `BUILD_BUG_ON` 的内容你可以在[内核初始化的第一部分](/Initialization/linux-initialization-1.md)看到。如果给定的表达式为真,`BUILD_BUG_ON` 宏就会抛出一个编译时错误。在检查后的下一步,我们可以看到对 `early_ioremap_setup` 函数的调用,这个函数定义在 [mm/early_ioremap.c](https://github.com/torvalds/linux/blob/master/mm/early_ioremap.c) 文件中。这个函数代表了对 `ioremap` 的大体初始化。`early_ioremap_setup` 函数用初期固定映射的地址填充了 `slot_virt` 数组。所有初期固定映射地址在内存中都在 `__end_of_permanent_fixed_addresses` 后面,它们从 `FIX_BITMAP_BEGIN` 开始,到 `FIX_BITMAP_END` 结束。实际上初期 `ioremap` 会使用 `512` 个临时引导时映射: ``` #define NR_FIX_BTMAPS 64 @@ -319,7 +319,7 @@ pmd_populate_kernel(&init_mm, pmd, bm_pte); `pmd_populate_kernel` 函数有三个参数: -* `init_mm` - `init` 进程的内存描述符 (你可以在[前文](http://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-5.html)中看到); +* `init_mm` - `init` 进程的内存描述符 (你可以在[前文](/Initialization/linux-initialization-5.md)中看到); * `pmd` - `ioremap` 固定映射开始处的页中部目录; * `bm_pte` - 初期 `ioremap` 页表入口数组定义为: @@ -519,5 +519,5 @@ prev_map[slot] = NULL; * [e820](http://en.wikipedia.org/wiki/E820) * [Memory management unit](http://en.wikipedia.org/wiki/Memory_management_unit) * [TLB](http://en.wikipedia.org/wiki/Translation_lookaside_buffer) -* [Paging](http://xinqiu.gitbooks.io/linux-insides-cn/content/Theory/linux-theory-1.html) -* [内核内存管理第一部分](http://xinqiu.gitbooks.io/linux-insides-cn/content/MM/linux-mm-1.html) +* [Paging](/Theory/linux-theory-1.md) +* [内核内存管理第一部分](/MM/linux-mm-1.md) diff --git a/MM/linux-mm-3.md b/MM/linux-mm-3.md index e4ab794..213ca3a 100644 --- a/MM/linux-mm-3.md +++ b/MM/linux-mm-3.md @@ -4,7 +4,7 @@ Linux内核内存管理 第三节 内核中 kmemcheck 介绍 -------------------------------------------------------------------------------- -Linux内存管理[章节](https://xinqiu.gitbooks.io/linux-insides-cn/content/MM/)描述了Linux内核中[内存管理](https://en.wikipedia.org/wiki/Memory_management);本小节是第三部分。 在本章[第二节](https://xinqiu.gitbooks.io/linux-insides-cn/content/MM/linux-mm-2.html)中我们遇到了两个与内存管理相关的概念: +Linux内存管理[章节](/MM/)描述了Linux内核中[内存管理](https://en.wikipedia.org/wiki/Memory_management);本小节是第三部分。 在本章[第二节](/MM/linux-mm-2.md)中我们遇到了两个与内存管理相关的概念: * `固定映射地址`; * `输入输出重映射`. @@ -62,11 +62,11 @@ $ sudo cat /proc/ioports ... ``` -`ioports` 的输出列出了系统中物理设备所注册的各种类型的I/O端口。内核不能直接访问设备的输入/输出地址。在内核能够使用这些内存之前,必须将这些地址映射到虚拟地址空间,这就是`io remap`机制的主要目的。在前面[第二节](https://xinqiu.gitbooks.io/linux-insides-cn/content/MM/linux-mm-2.html)中只介绍了早期的 `io remap` 。很快我们就要来看一看常规的 `io remap` 实现机制。但在此之前,我们需要学习一些其他的知识,例如不同类型的内存分配器等,不然的话我们很难理解该机制。 +`ioports` 的输出列出了系统中物理设备所注册的各种类型的I/O端口。内核不能直接访问设备的输入/输出地址。在内核能够使用这些内存之前,必须将这些地址映射到虚拟地址空间,这就是`io remap`机制的主要目的。在前面[第二节](/MM/linux-mm-2.md)中只介绍了早期的 `io remap` 。很快我们就要来看一看常规的 `io remap` 实现机制。但在此之前,我们需要学习一些其他的知识,例如不同类型的内存分配器等,不然的话我们很难理解该机制。 在进入Linux内核常规期的[内存管理](https://en.wikipedia.org/wiki/Memory_management)之前,我们要看一些特殊的内存机制,例如[调试](https://en.wikipedia.org/wiki/Debugging),检查[内存泄漏](https://en.wikipedia.org/wiki/Memory_leak),内存控制等等。学习这些内容有助于我们理解Linux内核的内存管理。 -从本节的标题中,你可能已经看出来,我们会从[kmemcheck](https://www.kernel.org/doc/Documentation/kmemcheck.txt)开始了解内存机制。和前面的[章节](https://xinqiu.gitbooks.io/linux-insides-cn/content/)一样,我们首先从理论上学习什么是 `kmemcheck` ,然后再来看Linux内核中是怎么实现这一机制的。 +从本节的标题中,你可能已经看出来,我们会从[kmemcheck](https://www.kernel.org/doc/Documentation/kmemcheck.txt)开始了解内存机制。和前面的[章节](/SUMMARY.md)一样,我们首先从理论上学习什么是 `kmemcheck` ,然后再来看Linux内核中是怎么实现这一机制的。 让我们开始吧。Linux内核中的 `kmemcheck` 到底是什么呢?从该机制的名称上你可能已经猜到, `kmemcheck` 是检查内存的。你猜的很对。`kmemcheck` 的主要目的就是用来检查是否有内核代码访问 `未初始化的内存` 。让我们看一个简单的 [C](https://en.wikipedia.org/wiki/C_%28programming_language%29) 程序: @@ -148,7 +148,7 @@ config X86 struct my_struct *my_struct = kmalloc(sizeof(struct my_struct), GFP_KERNEL); ``` -或者换句话说,在内核访问 [page](https://en.wikipedia.org/wiki/Page_%28computer_memory%29) 时会发生[缺页中断](https://en.wikipedia.org/wiki/Page_fault)。这是由于 `kmemcheck` 将内存页标记为`不存在`(关于Linux内存分页的相关信息,你可以参考[分页](https://0xax.gitbooks.io/linux-insides/content/Theory/linux-theory-1.html))。如果一个`缺页中断`异常发生了,异常处理程序会来处理这个异常,如果异常处理程序检测到内核使能了 `kmemcheck`,那么就会将控制权提交给 `kmemcheck` 来处理;`kmemcheck` 检查完之后,该内存页会被标记为 `present`,然后被中断的程序得以继续执行下去。 这里的处理方式比较巧妙,被中断程序的第一条指令执行时,`kmemcheck` 又会标记内存页为 `not present`,按照这种方式,下一个对内存页的访问也会被捕获。 +或者换句话说,在内核访问 [page](https://en.wikipedia.org/wiki/Page_%28computer_memory%29) 时会发生[缺页中断](https://en.wikipedia.org/wiki/Page_fault)。这是由于 `kmemcheck` 将内存页标记为`不存在`(关于Linux内存分页的相关信息,你可以参考[分页](/Theory/linux-theory-1.md))。如果一个`缺页中断`异常发生了,异常处理程序会来处理这个异常,如果异常处理程序检测到内核使能了 `kmemcheck`,那么就会将控制权提交给 `kmemcheck` 来处理;`kmemcheck` 检查完之后,该内存页会被标记为 `present`,然后被中断的程序得以继续执行下去。 这里的处理方式比较巧妙,被中断程序的第一条指令执行时,`kmemcheck` 又会标记内存页为 `not present`,按照这种方式,下一个对内存页的访问也会被捕获。 目前我们只是从理论层面考察了 `kmemcheck`,接下来我们看一下Linux内核是怎么来实现该机制的。 @@ -167,7 +167,7 @@ struct my_struct *my_struct = kmalloc(sizeof(struct my_struct), GFP_KERNEL); ![kernel configuration menu](http://oi66.tinypic.com/y2eeh.jpg) -从Linux初始化过程章节的第七节 [part](https://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-7.html) 中,我们知道在内核初始化过程中,会在 `do_initcall_level` , `do_early_param` 等函数中解析内核 command line。前面也提到过 `kmemcheck` 子系统由两部分组成,第一部分启动比较早。在源码 [mm/kmemcheck.c](https://github.com/torvalds/linux/blob/master/mm/kmemcheck.c) 中有一个函数 `param_kmemcheck` ,该函数在command line解析时就会用到: +从Linux初始化过程章节的第七节 [part](/Initialization/linux-initialization-7.md) 中,我们知道在内核初始化过程中,会在 `do_initcall_level` , `do_early_param` 等函数中解析内核 command line。前面也提到过 `kmemcheck` 子系统由两部分组成,第一部分启动比较早。在源码 [mm/kmemcheck.c](https://github.com/torvalds/linux/blob/master/mm/kmemcheck.c) 中有一个函数 `param_kmemcheck` ,该函数在command line解析时就会用到: ```C static int __init param_kmemcheck(char *str) @@ -190,7 +190,7 @@ early_param("kmemcheck", param_kmemcheck); 从前面的介绍我们知道 `param_kmemcheck` 可能存在三种情况:`0` (使能), `1` (禁止) or `2` (一次性)。 `param_kmemcheck` 的实现很简单:将command line传递的 `kmemcheck` 参数的值由字符串转换为整数,然后赋值给变量 `kmemcheck_enabled` 。 -第二阶段在内核初始化阶段执行,而不是在早期初始化过程 [initcalls](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-3.html) 。第二阶断的过程体现在 `kmemcheck_init` : +第二阶段在内核初始化阶段执行,而不是在早期初始化过程 [initcalls](/Concepts/linux-cpu-3.md) 。第二阶断的过程体现在 `kmemcheck_init` : ```C int __init kmemcheck_init(void) @@ -278,7 +278,7 @@ void kmemcheck_hide_pages(struct page *p, unsigned int n) 该函数遍历参数代表的所有内存页,并尝试获取每个内存页的 `页表项` 。如果获取成功,清理页表项的present 标记,设置页表项的 hidden 标记。在最后还需要刷新 [TLB](https://en.wikipedia.org/wiki/Translation_lookaside_buffer) ,因为有一些内存页已经发生了改变。从这个地方开始,内存页就进入 `kmemcheck` 的跟踪系统。由于内存页的 `present` 标记被清除了,一旦 `kmalloc` 返回了内存地址,并且有代码访问这个地址,就会触发[缺页中断](https://en.wikipedia.org/wiki/Page_fault)。 -在Linux内核初始化的[第二节](https://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-2.html)介绍过,`缺页中断`处理程序是 [arch/x86/mm/fault.c](https://github.com/torvalds/linux/blob/master/arch/x86/mm/fault.c) 的 `do_page_fault` 函数。该函数开始部分如下: +在Linux内核初始化的[第二节](/Initialization/linux-initialization-2.md)介绍过,`缺页中断`处理程序是 [arch/x86/mm/fault.c](https://github.com/torvalds/linux/blob/master/arch/x86/mm/fault.c) 的 `do_page_fault` 函数。该函数开始部分如下: ```C static noinline void @@ -296,7 +296,7 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code, } ``` -`kmemcheck_active` 函数获取 `kmemcheck_context` [per-cpu](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-1.html) 结构体,并返回该结构体成员 `balance` 和0的比较结果: +`kmemcheck_active` 函数获取 `kmemcheck_context` [per-cpu](/Concepts/linux-cpu-1.md) 结构体,并返回该结构体成员 `balance` 和0的比较结果: ``` bool kmemcheck_active(struct pt_regs *regs) @@ -337,7 +337,7 @@ if (!pte) static struct kmemcheck_error error_fifo[CONFIG_KMEMCHECK_QUEUE_SIZE]; ``` -`kmemcheck` 声明了一个特殊的 [tasklet](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-9.html) : +`kmemcheck` 声明了一个特殊的 [tasklet](/Interrupts/linux-interrupts-9.md) : ```C static DECLARE_TASKLET(kmemcheck_tasklet, &do_wakeup, 0); @@ -423,11 +423,11 @@ Links * [kmemcheck documentation](https://www.kernel.org/doc/Documentation/kmemcheck.txt) * [valgrind](https://en.wikipedia.org/wiki/Valgrind) * [page fault](https://en.wikipedia.org/wiki/Page_fault) -* [initcalls](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-3.html) +* [initcalls](/Concepts/linux-cpu-3.md) * [opcode](https://en.wikipedia.org/wiki/Opcode) * [translation lookaside buffer](https://en.wikipedia.org/wiki/Translation_lookaside_buffer) -* [per-cpu variables](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-1.html) +* [per-cpu variables](/Concepts/linux-cpu-1.md) * [flags register](https://en.wikipedia.org/wiki/FLAGS_register) -* [tasklet](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-9.html) -* [Paging](https://xinqiu.gitbooks.io/linux-insides-cn/content/Theory/linux-theory-1.html) -* [Previous part](https://xinqiu.gitbooks.io/linux-insides-cn/content/MM/linux-mm-2.html) +* [tasklet](/Interrupts/linux-interrupts-9.md) +* [Paging](/Theory/linux-theory-1.md) +* [Previous part](/MM/linux-mm-2.md)