Translated by qhwdw

This commit is contained in:
qhwdw 2018-01-12 12:30:40 +08:00
parent b8f5b05111
commit 05ee537221
2 changed files with 103 additions and 104 deletions

View File

@ -1,104 +0,0 @@
Translating by qhwdw
How the Kernel Manages Your Memory
============================================================
After examining the [virtual address layout][1] of a process, we turn to the kernel and its mechanisms for managing user memory. Here is gonzo again:
![Linux kernel mm_struct](http://static.duartes.org/img/blogPosts/mm_struct.png)
Linux processes are implemented in the kernel as instances of [task_struct][2], the process descriptor. The [mm][3] field in task_struct points to the memory descriptor, [mm_struct][4], which is an executive summary of a programs memory. It stores the start and end of memory segments as shown above, the [number][5] of physical memory pages used by the process (rss stands for Resident Set Size), the [amount][6] of virtual address space used, and other tidbits. Within the memory descriptor we also find the two work horses for managing program memory: the set of virtual memory areas and the page tables. Gonzos memory areas are shown below:
![Kernel memory descriptor and memory areas](http://static.duartes.org/img/blogPosts/memoryDescriptorAndMemoryAreas.png)
Each virtual memory area (VMA) is a contiguous range of virtual addresses; these areas never overlap. An instance of [vm_area_struct][7] fully describes a memory area, including its start and end addresses, [flags][8] to determine access rights and behaviors, and the [vm_file][9] field to specify which file is being mapped by the area, if any. A VMA that does not map a file is anonymous. Each memory segment above ( _e.g._ , heap, stack) corresponds to a single VMA, with the exception of the memory mapping segment. This is not a requirement, though it is usual in x86 machines. VMAs do not care which segment they are in.
A programs VMAs are stored in its memory descriptor both as a linked list in the [mmap][10] field, ordered by starting virtual address, and as a [red-black tree][11] rooted at the [mm_rb][12] field. The red-black tree allows the kernel to search quickly for the memory area covering a given virtual address. When you read file `/proc/pid_of_process/maps`, the kernel is simply going through the linked list of VMAs for the process and [printing each one][13].
In Windows, the [EPROCESS][14] block is roughly a mix of task_struct and mm_struct. The Windows analog to a VMA is the Virtual Address Descriptor, or [VAD][15]; they are stored in an [AVL tree][16]. You know what the funniest thing about Windows and Linux is? Its the little differences.
The 4GB virtual address space is divided into pages. x86 processors in 32-bit mode support page sizes of 4KB, 2MB, and 4MB. Both Linux and Windows map the user portion of the virtual address space using 4KB pages. Bytes 0-4095 fall in page 0, bytes 4096-8191 fall in page 1, and so on. The size of a VMA  _must be a multiple of page size_ . Heres 3GB of user space in 4KB pages:
![4KB Pages Virtual User Space](http://static.duartes.org/img/blogPosts/pagedVirtualSpace.png)
The processor consults page tables to translate a virtual address into a physical memory address. Each process has its own set of page tables; whenever a process switch occurs, page tables for user space are switched as well. Linux stores a pointer to a process page tables in the [pgd][17] field of the memory descriptor. To each virtual page there corresponds one page table entry (PTE) in the page tables, which in regular x86 paging is a simple 4-byte record shown below:
![x86 Page Table Entry (PTE) for 4KB page](http://static.duartes.org/img/blogPosts/x86PageTableEntry4KB.png)
Linux has functions to [read][18] and [set][19] each flag in a PTE. Bit P tells the processor whether the virtual page is present in physical memory. If clear (equal to 0), accessing the page triggers a page fault. Keep in mind that when this bit is zero, the kernel can do whatever it pleases with the remaining fields. The R/W flag stands for read/write; if clear, the page is read-only. Flag U/S stands for user/supervisor; if clear, then the page can only be accessed by the kernel. These flags are used to implement the read-only memory and protected kernel space we saw before.
Bits D and A are for dirty and accessed. A dirty page has had a write, while an accessed page has had a write or read. Both flags are sticky: the processor only sets them, they must be cleared by the kernel. Finally, the PTE stores the starting physical address that corresponds to this page, aligned to 4KB. This naive-looking field is the source of some pain, for it limits addressable physical memory to [4 GB][20]. The other PTE fields are for another day, as is Physical Address Extension.
A virtual page is the unit of memory protection because all of its bytes share the U/S and R/W flags. However, the same physical memory could be mapped by different pages, possibly with different protection flags. Notice that execute permissions are nowhere to be seen in the PTE. This is why classic x86 paging allows code on the stack to be executed, making it easier to exploit stack buffer overflows (its still possible to exploit non-executable stacks using [return-to-libc][21] and other techniques). This lack of a PTE no-execute flag illustrates a broader fact: permission flags in a VMA may or may not translate cleanly into hardware protection. The kernel does what it can, but ultimately the architecture limits what is possible.
Virtual memory doesnt store anything, it simply  _maps_  a programs address space onto the underlying physical memory, which is accessed by the processor as a large block called the physical address space. While memory operations on the bus are [somewhat involved][22], we can ignore that here and assume that physical addresses range from zero to the top of available memory in one-byte increments. This physical address space is broken down by the kernel into page frames. The processor doesnt know or care about frames, yet they are crucial to the kernel because the page frame is the unit of physical memory management. Both Linux and Windows use 4KB page frames in 32-bit mode; here is an example of a machine with 2GB of RAM:
![Physical Address Space](http://static.duartes.org/img/blogPosts/physicalAddressSpace.png)
In Linux each page frame is tracked by a [descriptor][23] and [several flags][24]. Together these descriptors track the entire physical memory in the computer; the precise state of each page frame is always known. Physical memory is managed with the [buddy memory allocation][25]technique, hence a page frame is free if its available for allocation via the buddy system. An allocated page frame might be anonymous, holding program data, or it might be in the page cache, holding data stored in a file or block device. There are other exotic page frame uses, but leave them alone for now. Windows has an analogous Page Frame Number (PFN) database to track physical memory.
Lets put together virtual memory areas, page table entries and page frames to understand how this all works. Below is an example of a user heap:
![Physical Address Space](http://static.duartes.org/img/blogPosts/heapMapped.png)
Blue rectangles represent pages in the VMA range, while arrows represent page table entries mapping pages onto page frames. Some virtual pages lack arrows; this means their corresponding PTEs have the Present flag clear. This could be because the pages have never been touched or because their contents have been swapped out. In either case access to these pages will lead to page faults, even though they are within the VMA. It may seem strange for the VMA and the page tables to disagree, yet this often happens.
A VMA is like a contract between your program and the kernel. You ask for something to be done (memory allocated, a file mapped, etc.), the kernel says “sure”, and it creates or updates the appropriate VMA. But  _it does not_  actually honor the request right away, it waits until a page fault happens to do real work. The kernel is a lazy, deceitful sack of scum; this is the fundamental principle of virtual memory. It applies in most situations, some familiar and some surprising, but the rule is that VMAs record what has been  _agreed upon_ , while PTEs reflect what has  _actually been done_  by the lazy kernel. These two data structures together manage a programs memory; both play a role in resolving page faults, freeing memory, swapping memory out, and so on. Lets take the simple case of memory allocation:
![Example of demand paging and memory allocation](http://static.duartes.org/img/blogPosts/heapAllocation.png)
When the program asks for more memory via the [brk()][26] system call, the kernel simply [updates][27]the heap VMA and calls it good. No page frames are actually allocated at this point and the new pages are not present in physical memory. Once the program tries to access the pages, the processor page faults and [do_page_fault()][28] is called. It [searches][29] for the VMA covering the faulted virtual address using [find_vma()][30]. If found, the permissions on the VMA are also checked against the attempted access (read or write). If theres no suitable VMA, no contract covers the attempted memory access and the process is punished by Segmentation Fault.
When a VMA is [found][31] the kernel must [handle][32] the fault by looking at the PTE contents and the type of VMA. In our case, the PTE shows the page is [not present][33]. In fact, our PTE is completely blank (all zeros), which in Linux means the virtual page has never been mapped. Since this is an anonymous VMA, we have a purely RAM affair that must be handled by [do_anonymous_page()][34], which allocates a page frame and makes a PTE to map the faulted virtual page onto the freshly allocated frame.
Things could have been different. The PTE for a swapped out page, for example, has 0 in the Present flag but is not blank. Instead, it stores the swap location holding the page contents, which must be read from disk and loaded into a page frame by [do_swap_page()][35] in what is called a [major fault][36].
This concludes the first half of our tour through the kernels user memory management. In the next post, well throw files into the mix to build a complete picture of memory fundamentals, including consequences for performance.
--------------------------------------------------------------------------------
via: http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/
作者:[Gustavo Duarte][a]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]:http://duartes.org/gustavo/blog/about/
[1]:http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory
[2]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/sched.h#L1075
[3]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/sched.h#L1129
[4]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L173
[5]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L197
[6]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L206
[7]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L99
[8]:http://lxr.linux.no/linux+v2.6.28/include/linux/mm.h#L76
[9]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L150
[10]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L174
[11]:http://en.wikipedia.org/wiki/Red_black_tree
[12]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L175
[13]:http://lxr.linux.no/linux+v2.6.28.1/fs/proc/task_mmu.c#L201
[14]:http://www.nirsoft.net/kernel_struct/vista/EPROCESS.html
[15]:http://www.nirsoft.net/kernel_struct/vista/MMVAD.html
[16]:http://en.wikipedia.org/wiki/AVL_tree
[17]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L185
[18]:http://lxr.linux.no/linux+v2.6.28.1/arch/x86/include/asm/pgtable.h#L173
[19]:http://lxr.linux.no/linux+v2.6.28.1/arch/x86/include/asm/pgtable.h#L230
[20]:http://www.google.com/search?hl=en&q=2^20+*+2^12+bytes+in+GB
[21]:http://en.wikipedia.org/wiki/Return-to-libc_attack
[22]:http://duartes.org/gustavo/blog/post/getting-physical-with-memory
[23]:http://lxr.linux.no/linux+v2.6.28/include/linux/mm_types.h#L32
[24]:http://lxr.linux.no/linux+v2.6.28/include/linux/page-flags.h#L14
[25]:http://en.wikipedia.org/wiki/Buddy_memory_allocation
[26]:http://www.kernel.org/doc/man-pages/online/pages/man2/brk.2.html
[27]:http://lxr.linux.no/linux+v2.6.28.1/mm/mmap.c#L2050
[28]:http://lxr.linux.no/linux+v2.6.28/arch/x86/mm/fault.c#L583
[29]:http://lxr.linux.no/linux+v2.6.28/arch/x86/mm/fault.c#L692
[30]:http://lxr.linux.no/linux+v2.6.28/mm/mmap.c#L1466
[31]:http://lxr.linux.no/linux+v2.6.28/arch/x86/mm/fault.c#L711
[32]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2653
[33]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2674
[34]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2681
[35]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2280
[36]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2316

View File

@ -0,0 +1,103 @@
内核如何管理内存
============================================================
在学习了进程的 [虚拟地址布局][1] 之后,我们回到内核,来学习它管理用户内存的机制。这里再次使用 Gonzo
![Linux kernel mm_struct](http://static.duartes.org/img/blogPosts/mm_struct.png)
Linux 进程在内核中是作为进程描述符 [task_struct][2] (译者注:它是在 Linux 中描述进程完整信息的一种数据结构)的实例来实现的。在 task_struct 中的  [mm][3]  域指向到内存描述符,[mm_struct][4] 是一个程序在内存中的执行摘要。它保存了起始和结束内存段,如下图所示,进程使用的物理内存页面的 [数量][5]RSS 译者注Resident Set Size常驻内存大小 、虚拟地址空间使用的 [总数量][6]、以及其它片断。 在内存描述中我们可以获悉它有两种管理内存的方式虚拟内存区域集和页面表。Gonzo 的内存区域如下所示:
![Kernel memory descriptor and memory areas](http://static.duartes.org/img/blogPosts/memoryDescriptorAndMemoryAreas.png)
每个虚拟内存区域VMA是一个连续的虚拟地址范围这些区域绝对不会重叠。一个 [vm_area_struct][7] 的实例完整描述了一个内存区域,包括它的起始和结束地址,[flags][8] 决定了访问权限和行为,并且 [vm_file][9] 域指定了映射到这个区域的文件(如果有的话)。除了内存映射段的例外情况之外,一个 VMA 是不能匿名映射文件的,上面的每个内存段(比如,堆、栈)都对应一个单个的 VMA。虽然它通常都使用在 x86 的机器上但它并不是必需的。VMAs 也不关心它们在哪个段中。
一个程序的 VMAs 在内存描述符中作为 [mmap][10] 域的一个链接列表保存的,以起始虚拟地址为序进行排列,并且在 [mm_rb][12] 域中作为一个 [红黑树][11] 的根。红黑树允许内核通过给定的虚拟地址去快速搜索内存区域。在你读取文件 `/proc/pid_of_process/maps`时,内核只是简单地读取每个进程的 VMAs 的链接列表并显示它们。
在 Windows 中,[EPROCESS][14] 块大致类似于一个 task_struct 和 mm_struct 的结合。在 Windows 中模拟一个 VMA 的是虚拟地址描述符,或称为 [VAD][15];它保存在一个 [AVL 树][16] 中。你知道关于 Windows 和 Linux 之间最有趣的事情是什么吗?其实它们只有一点小差别。
4GB 虚拟地址空间被分为两个页面。在 32 位模式中的 x86 处理器中支持 4KB、2MB、以及 4MB 大小的页面。Linux 和 Windows 都使用大小为 4KB 的页面去映射用户的一部分虚拟地址空间。字节 0-4095 在 page 0 中,字节 4096-8191 在 page 1 中依次类推。VMA 的大小 _必须是页面大小的倍数_ 。下图是使用 4KB 大小页面的总数量为 3GB 的用户空间:
![4KB Pages Virtual User Space](http://static.duartes.org/img/blogPosts/pagedVirtualSpace.png)
处理器通过查看页面表去转换一个虚拟内存地址到一个真实的物理内存地址。每个进程都有它自己的一组页面表每当发生进程切换时用户空间的页面表也同时切换。Linux 在内存描述符的 [pgd][17] 域中保存了一个指向处理器页面表的指针。对于每个虚拟页面页面表中都有一个相应的页面表条目PTE在常规的 x86 页面表中,它是一个简单的如下所示的大小为 4 字节的一条记录:
![x86 Page Table Entry (PTE) for 4KB page](http://static.duartes.org/img/blogPosts/x86PageTableEntry4KB.png)
Linux 通过函数去 [读取][18] 和 [设置][19]  PTE 条目中的每个标志位。标志位 P 告诉处理器这个虚拟页面是否在物理内存中。如果被清除(设置为 0访问这个页面将触发一个页面故障。请记住当这个标志位为 0 时内核可以在剩余的域上做任何想做的事。R/W 标志位是读/写标志如果被清除这个页面将变成只读的。U/S 标志位表示用户/超级用户;如果被清除,这个页面将仅被内核访问。这些标志都是用于实现我们在前面看到的只读内存和内核空间保护。
标志位 D 和 A 用于标识页面是否是“脏的”或者是已被访问过。一个脏页面表示已经被写入而一个被访问过的页面则表示有一个写入或者读取发生过。这两个标志位都是粘滞位处理器只能设置它们而清除则是由内核来完成的。最终PTE 保存了这个页面相应的起始物理地址,它们按 4KB 进行整齐排列。这个看起来有点小的域是一些痛苦的根源,因为它限制了物理内存最大为 [4 GB][20]。其它的 PTE 域留到下次再讲,因为它是涉及了物理地址扩展的知识。
由于在一个虚拟页面上的所有字节都共享一个 U/S 和 R/W 标志位,所以内存保护的最小单元是一个虚拟页面。但是,同一个物理内存可能被映射到不同的虚拟页面,这样就有可能会出现相同的物理内存出现不同的保护标志位的情况。请注意,在 PTE 中是看不到运行权限的。这就是为什么经典的 x86 页面上允许代码在栈上被执行的原因,这样会很容易导致挖掘栈缓冲溢出的漏洞(可能会通过使用 [return-to-libc][21] 和其它技术来开发非可执行栈)。由于 PTE 缺少禁止运行标志位说明了一个更广泛的事实:在 VMA 中的权限标志位有可能或者不可能完全转换为硬件保护。内核只能做它能做到的,但是,最终的架构限制了它能做的事情。
虚拟内存不能保存任何东西,它只是简单地  _映射_  一个程序的地址空间到底层的物理内存上。物理内存被当作一个被称为物理地址空间的巨大块来被处理器访问。虽然内存的操作[涉及到某些][22] 总线,我们在这里先忽略它,并假设物理地址范围从 0 到可用的最大值按字节递增。物理地址空间被内核进一步分解为页面帧。处理器并不会关心帧的具体情况这一点对内核也是至关重要的因为页面帧是物理内存管理的最小单元。Linux 和 Windows 在 32 位模式下都使用 4KB 大小的页面帧;下图是一个有 2 GB 内存的机器的例子:
![Physical Address Space](http://static.duartes.org/img/blogPosts/physicalAddressSpace.png)
在 Linux 上每个页面帧是被一个 [描述符][23] 和 [几个标志][24] 来跟踪的。通过这些描述符和标志,实现了对机器上整个物理内存的跟踪;每个页面帧的具体状态是公开的。物理内存是通过使用 [Buddy 内存分配][25] (译者注:一种内存分配算法)技术来管理的,因此,如果可以通过 Buddy 系统分配内存那么一个页面帧是未分配的free。一个被分配的页面帧可以是匿名的、持有程序数据的、或者它可能处于页面缓存中、持有数据保存在一个文件或者块设备中。还有其它的异形页面帧但是这些异形页面帧现在已经不怎么使用了。Windows 有一个类似的页面帧号Page Frame Number (PFN))数据库去跟踪物理内存。
我们把虚拟内存区域VMA、页面表条目PTE、以及页面帧放在一起来理解它们是如何工作的。下面是一个用户堆的示例
![Physical Address Space](http://static.duartes.org/img/blogPosts/heapMapped.png)
蓝色的矩形框表示在 VMA 范围内的页面,而箭头表示页面表条目映射页面到页面帧。一些缺少箭头的虚拟页面,表示它们对应的 PTEs 的当前标志位被清除(置为 0。这可能是因为这个页面从来没有被使用过或者是它的内容已经被交换出去了swapped out。在这两种情况下即便这些页面在 VMA 中,访问它们也将导致产生一个页面故障。对于这种 VMA 和页面表的不一致的情况,看上去似乎很奇怪,但是这种情况却经常发生。
一个 VMA 像一个在你的程序和内核之间的合约。你请求它做一些事情(分配内存、文件映射、等等),内核会回应“收到”,然后去创建或者更新相应的 VMA。 但是,它 _并不立刻_ 去“兑现”对你的承诺,而是它会等待到发生一个页面故障时才去 _真正_ 做这个工作。内核是个“懒惰的家伙”、“不诚实的人渣”这就是虚拟内存的基本原理。它适用于大多数的、一些类似的和意外的情况但是它是规则是VMAs 记录 _约定的_ 内容,而 PTEs 才反映这个“懒惰的内核”  _真正做了什么_。通过这两种数据结构共同来管理程序的内存;它们共同来完成解决页面故障、释放内存、从内存中交换出数据、等等。下图是内存分配的一个简单案例:
![Example of demand paging and memory allocation](http://static.duartes.org/img/blogPosts/heapAllocation.png)
当程序通过 [brk()][26] 系统调用来请求一些内存时,内核只是简单地 [更新][27] 堆的 VMA 并给程序回复“已搞定”。而在这个时候并没有真正地分配页面帧并且新的页面也没有映射到物理内存上。一旦程序尝试去访问这个页面时,将发生页面故障,然后处理器调用 [do_page_fault()][28]。这个函数将使用 [find_vma()][30] 去  [搜索][29] 发生页面故障的 VMA。如果找到了然后在 VMA 上进行权限检查以防范恶意访问(读取或者写入)。如果没有合适的 VMA也没有尝试访问的内存的“合约”将会给进程返回段故障。
当找到了一个合适的 VMA内核必须通过查找 PTE 的内容和 VMA 的类型去处理故障。在我们的案例中PTE 显示这个页面是 [不存在的][33]。事实上,我们的 PTE 是全部空白的(全部都是 0在 Linux 中这表示虚拟内存还没有被映射。由于这是匿名 VMA我们有一个完全的 RAM 事务,它必须被 [do_anonymous_page()][34] 来处理,它分配页面帧,并且用一个 PTE 去映射故障虚拟页面到一个新分配的帧。
有时候,事情可能会有所不同。例如,对于被交换出内存的页面的 PTE在当前Present标志位上是 0但它并不是空白的。而是在交换位置仍有页面内容它必须从磁盘上读取并且通过 [do_swap_page()][35] 来加载到一个被称为 [major fault][36] 的页面帧上。
这是我们通过探查内核的用户内存管理得出的前半部分的结论。在下一篇文章中,我们通过将文件加载到内存中,来构建一个完整的内存框架图,以及对性能的影响。
--------------------------------------------------------------------------------
via: http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/
作者:[Gustavo Duarte][a]
译者:[qhwdw](https://github.com/qhwdw)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]:http://duartes.org/gustavo/blog/about/
[1]:http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory
[2]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/sched.h#L1075
[3]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/sched.h#L1129
[4]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L173
[5]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L197
[6]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L206
[7]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L99
[8]:http://lxr.linux.no/linux+v2.6.28/include/linux/mm.h#L76
[9]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L150
[10]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L174
[11]:http://en.wikipedia.org/wiki/Red_black_tree
[12]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L175
[13]:http://lxr.linux.no/linux+v2.6.28.1/fs/proc/task_mmu.c#L201
[14]:http://www.nirsoft.net/kernel_struct/vista/EPROCESS.html
[15]:http://www.nirsoft.net/kernel_struct/vista/MMVAD.html
[16]:http://en.wikipedia.org/wiki/AVL_tree
[17]:http://lxr.linux.no/linux+v2.6.28.1/include/linux/mm_types.h#L185
[18]:http://lxr.linux.no/linux+v2.6.28.1/arch/x86/include/asm/pgtable.h#L173
[19]:http://lxr.linux.no/linux+v2.6.28.1/arch/x86/include/asm/pgtable.h#L230
[20]:http://www.google.com/search?hl=en&q=2^20+*+2^12+bytes+in+GB
[21]:http://en.wikipedia.org/wiki/Return-to-libc_attack
[22]:http://duartes.org/gustavo/blog/post/getting-physical-with-memory
[23]:http://lxr.linux.no/linux+v2.6.28/include/linux/mm_types.h#L32
[24]:http://lxr.linux.no/linux+v2.6.28/include/linux/page-flags.h#L14
[25]:http://en.wikipedia.org/wiki/Buddy_memory_allocation
[26]:http://www.kernel.org/doc/man-pages/online/pages/man2/brk.2.html
[27]:http://lxr.linux.no/linux+v2.6.28.1/mm/mmap.c#L2050
[28]:http://lxr.linux.no/linux+v2.6.28/arch/x86/mm/fault.c#L583
[29]:http://lxr.linux.no/linux+v2.6.28/arch/x86/mm/fault.c#L692
[30]:http://lxr.linux.no/linux+v2.6.28/mm/mmap.c#L1466
[31]:http://lxr.linux.no/linux+v2.6.28/arch/x86/mm/fault.c#L711
[32]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2653
[33]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2674
[34]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2681
[35]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2280
[36]:http://lxr.linux.no/linux+v2.6.28/mm/memory.c#L2316