Linux用户进程内存分配及二级页表PTE的二三事
<br />Linux用户进程内存分配及二级页表PTE的二三事<br />我们在用调试器看Linux用户进程代码时,发现了一件很有意思的事情,在一段内存空间中,有一整页(4K)都是data abort,如下:<br />第一页4011c000数据正常<br />... ...<br />4011cfec[0xe28dd014] add r13,r13,#0x14<br />4011cff
Linux 用户进程内存分配及二级页表 PTE 的二三事
我们在用调试器看 Linux 用户进程代码时,发现了一件很有意思的事情,在一段内存空间中,有一整页( 4K )都是 data abort ,如下:
第一页 4011c 000 数据正常
... ...
4011cfec [0xe28dd014] add r13,r13,#0x14
4011cff0 [0xe8bd40f0] ldmfd r13!,{r4-r7,r14}
4011cff4 [0xe12fff1e] bx r14
4011cff8 [0xe92d41f0] stmfd r13!,{r4-r8,r14}
4011cffc [0xe59f4064] ldr r4,0x4011d068
第二页 4011d000 都是 Data abort
4011d000 *** Data abort ***
4011d004 *** Data abort ***
4011d008 *** Data abort ***
4011d00c *** Data abort ***
... ...
第三页 4011e000 数据正常
4011e000 数据正常
由于当时并不知道 Linux 是如何处理用户进程的内存分配,所以认为这是一个“错误”。既然有错误,我们决定找到这个问题发生的根源。
在追踪这个问题的过程中, leeming 同学做了一个很 BT 的实验。我简单说两句,详细大家可以去看他的文章( http://blog.chinaunix.net/u3/99423/showart_2096904.html )。大概的方法就是将物理内存全部 dump 出来,通过第一页的代码,比如 0xe59f4064 ,查找其在内存中的物理地址,再通过提取物理地址的前 20 位,就可以查找 Linux 系统的二级页表(对应 ARM 的 TLB , Linux 中叫 PTE )。这个实验虽然看上去很不可思议,但实现起来并不复杂。最终得到了 Linux 存放在内存中的 TLB 数据。
每个表项是 32bit
虚拟页地址 对应表项的内容
4011c 000 3156caae
4011d000 00000000
4011e000 3156faae
很有意思, Data abort 的数据段,对应的 PTE 也是空的,这难道是一个系统错误?
NO !经过进一步的学习后发现,在 Linux 系统中,这是一个很正常的现象。 Linux 在用户进程执行时并没有建立所有内存页面的映射,而是需要用到的时候再建立映射关系。当 Linux 用户进程访问到没有建立映射的页表(此时 PTE 指针为 0 ),会调用相应的函数进行处理,或建立、或换出,具体执行这个操作的函数叫 handle_pte_fault (),位于内核的 mm/memory.c 中。
但是, Linux 是如何进入缺页处理的呢?
有两种情况,都是利用了 ARM 处理器的异常中断进行相应的处理。
第一种是程序顺序执行,正常页面的最后一条指令执行完后进入空页面,当空页面的第一条指令进入 ARM 处理器流水线的执行周期时, ARM 处理器会报告一个指令预取异常中断,并跳转到地址 0x0c ,在 Linux 系统中由于使用了高地址向量表,所以会跳转到 0xffff000c 。此时 ARM 处理器进入 ABORT 状态,执行一系列代码保存现场(代码位于 /arch/arm/kernel/entry-armv.s ),然后进入 SVC 状态执行 arch/arm/mm/fault.c 中的 do_PrefetchAbort (),最后会调用 handle_pte_fault ()处理缺页异常。
第二种情况,页面中的程序执行时需要使用未分配页面的数据,比如“ ldr r0, 未分配页地址”。遇到这种情况,就不是指令预取异常了,而是数据访问异常( Data abort )。此时处理器依然会进入 ABORT 状态,跳转到 0xffff0010 执行相应的 vector_dabt 代码( entry_armv.s )保存状态,进入 SVC 态,执行 do_DataAbort ()函数,最后同样调用 handle_pte_fault ()处理缺页异常。
因此,最开始遇到的情况:三个 PTE ,中间是空的,这是一个很正常的情况。因为第三页很可能由于前面的调用而已经建立,第二页却还没有建立。
至于 handle_pte_fault ()如何处理缺页异常,我还没有看完,就不在本文讨论了。已知至少有 do_no_page() 、 do_swap_page() 、 do_wp_page 等多种方式,此为后话。
通过跟踪用户程序,发现 Linux 用户进程基本所有的页面都是这样处理,因此处理器会很频繁的进出 Abort 状态,执行页面处理函数,这是会不会效率有点低了呢?待研究。
阿虚( Rockie Cheng )
2009-11-21
本文为原创,转载请注明出处http://hi.baidu.com/aokikyon
如有错误,欢迎指正!
更多推荐
所有评论(0)