📄 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 + -