📄 3.html
字号:
| Tasks | \ \ | Tasks |<br>
| __|___\_\__|__ Space |<br>
| | \ \ | |<br>
| | \ \|----------------|<br>
| | \ | 实际内核空间 |<br>
|________________| \|________________|<p>
逻辑地址 物理地址<p>
<p>
[内存实时分配]<p>
|copy_mm<br>
|allocate_mm = kmem_cache_alloc<br>
|__kmem_cache_alloc<br>
|kmem_cache_alloc_one<br>
|alloc_new_slab<br>
|kmem_cache_grow<br>
|kmem_getpages<br>
|__get_free_pages<br>
|alloc_pages<br>
|alloc_pages_pgdat<br>
|__alloc_pages<br>
|rmqueue<br>
|reclaim_pages<p>
·copy_mm [kernel/fork.c]<br>
·allocate_mm [kernel/fork.c]<br>
·kmem_cache_alloc [mm/slab.c]<br>
·__kmem_cache_alloc<br>
·kmem_cache_alloc_one<br>
·alloc_new_slab<br>
·kmem_cache_grow<br>
·kmem_getpages<br>
·__get_free_pages [mm/page_alloc.c]<br>
·alloc_pages [mm/numa.c]<br>
·alloc_pages_pgdat<br>
·__alloc_pages [mm/page_alloc.c]<br>
·rm_queue<br>
·reclaim_pages [mm/vmscan.c]<p>
[内存交换线程kswapd]<p>
|kswapd<br>
|// initialization routines<br>
|for (;;) { // Main loop<br>
|do_try_to_free_pages<br>
|recalculate_vm_stats<br>
|refill_inactive_scan<br>
|run_task_queue<br>
|interruptible_sleep_on_timeout // we sleep for a new swap request<br>
|}<p>
<br>
·kswapd [mm/vmscan.c]<br>
·do_try_to_free_pages<br>
·recalculate_vm_stats [mm/swap.c]<br>
·refill_inactive_scan [mm/vmswap.c]<br>
·run_task_queue [kernel/softirq.c]<br>
·interruptible_sleep_on_timeout [kernel/sched.c]<p>
<br>
[内存交换机制:出现内存不足的Exception]<p>
| Page Fault Exception<br>
| cause by all these conditions:<br>
| a-) User page<br>
| b-) Read or write access<br>
| c-) Page not present<br>
|<br>
|<br>
-----------> |do_page_fault<br>
|handle_mm_fault<br>
|pte_alloc<br>
|pte_alloc_one<br>
|__get_free_page = __get_free_pages<br>
|alloc_pages<br>
|alloc_pages_pgdat<br>
|__alloc_pages<br>
|wakeup_kswapd // We wake up kernel thread kswapd<p>
<br>
·do_page_fault [arch/i386/mm/fault.c]<br>
·handle_mm_fault [mm/memory.c]<br>
·pte_alloc<br>
·pte_alloc_one [include/asm/pgalloc.h]<br>
·__get_free_page [include/linux/mm.h]<br>
·__get_free_pages [mm/page_alloc.c]<br>
·alloc_pages [mm/numa.c]<br>
·alloc_pages_pgdat<br>
·__alloc_pages<br>
·wakeup_kswapd [mm/vmscan.c]
<center><A HREF="#Content">[目录]</A></center>
<hr><br><A NAME="I377" ID="I377"></A><center><b><font size=+2>内存管理子系统导读from aka</font></b></center><br>
我的目标是‘导读’,提供linux内存管理子系统的整体概念,同时给出进一步深入研究某个部分时的辅助信息(包括代码组织,文件和主要函数的意义和一些参考文档)。之所以采取这种方式,是因为我本人在阅读代码的过程中,深感“读懂一段代码容易,把握整体思想却极不容易”。而且,在我写一些内核代码时,也觉得很多情况下,不一定非得很具体地理解所有内核代码,往往了解它的接口和整体工作原理就够了。当然,我个人的能力有限,时间也很不够,很多东西也是近期迫于讲座压力临时学的:),内容难免偏颇甚至错误,欢迎大家指正。<p>
存储层次结构和x86存储管理硬件(MMU)<p>
这里假定大家对虚拟存储,段页机制有一定的了解。主要强调一些很重要的或者容易误解的概念。<p>
存储层次<p>
高速缓存(cache) --〉 主存(main memory) ---〉 磁盘(disk)<p>
理解存储层次结构的根源:CPU速度和存储器速度的差距。<p>
层次结构可行的原因:局部性原理。<p>
LINUX的任务:<p>
减小footprint,提高cache命中率,充分利用局部性。<p>
实现虚拟存储以满足进程的需求,有效地管理内存分配,力求最合理地利用有限的资源。<p>
参考文档:<p>
《too little,too small》by Rik Van Riel, Nov. 27,2000.<p>
以及所有的体系结构教材:)<p>
<br>
MMU的作用<p>
辅助操作系统进行内存管理,提供虚实地址转换等硬件支持。<p>
<br>
x86的地址<p>
逻辑地址: 出现在机器指令中,用来制定操作数的地址。段:偏移<p>
线性地址:逻辑地址经过分段单元处理后得到线性地址,这是一个32位的无符号整数,可用于定位4G个存储单元。<p>
物理地址:线性地址经过页表查找后得出物理地址,这个地址将被送到地址总线上指示所要访问的物理内存单元。<p>
LINUX: 尽量避免使用段功能以提高可移植性。如通过使用基址为0的段,使逻辑地址==线性地址。<p>
<br>
x86的段<p>
保护模式下的段:选择子+描述符。不仅仅是一个基地址的原因是为了提供更多的信息:保护、长度限制、类型等。描述符存放在一张表中(GDT或LDT),选择子可以认为是表的索引。段寄存器中存放的是选择子,在段寄存器装入的同时,描述符中的数据被装入一个不可见的寄存器以便cpu快速访问。(图)P40<p>
专用寄存器:GDTR(包含全局描述附表的首地址),LDTR(当前进程的段描述附表首地址),TSR(指向当前进程的任务状态段)<p>
<br>
LINUX使用的段:<p>
__KERNEL_CS: 内核代码段。范围 0-4G。可读、执行。DPL=0。<p>
__KERNEL_DS:内核代码段。范围 0-4G。可读、写。DPL=0。<p>
__USER_CS:内核代码段。范围 0-4G。可读、执行。DPL=3。<p>
__USER_DS:内核代码段。范围 0-4G。可读、写。DPL=3。<p>
TSS(任务状态段):存储进程的硬件上下文,进程切换时使用。(因为x86硬件对TSS有一定支持,所有有这个特殊的段和相应的专用寄存器。)<p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -