分类: 嵌入式
2011-01-02 09:42:45
存储管理分析札记 | |
1. MIPS 与 X86 的 TLB 差别 其在于对 TLB 不命中时的处理上: MIPS 会触发TLB Refill 异常,内核的 tlb_refill_handler 会以 pgd_current 为当前进程的
PGD 基址,索引获得转换失败的虚址对应的 PTE,并将其填入 TLB,完了CPU 把刚刚转换失败的虚地址再走一下 TLB 就OK了。 而 X86 在 TLB 不命中时,是由硬件 MMU 以 CR3 为当前进程的 PGD 基址,索引获得 PFN 后,直接输出 PA。同时 MMU 会填充 TLB 以加快下次转换的速度。 另外转换失败的虚址,MIPS 使用 BadVAddr 寄存器存放,X86 使用 CR2 存放。 可以看到,只要进程数目固定页目录表所需的空间是固定的,每个进程页目录表为一个页大小;而进程总页表的大小要依赖于进程代码、数据的大小。 一个典型的 MIPS32 系统,页大小为4KB,单用户模式,仅有bash进程,其页表大小为 48KB 左右(cat /proc/meminfo|grep PageTables);页目录的大小可由下式计算: PGD_SIZE = NR_PROCESS(exclude kernel thread) * PAGE_SIZE 而 Page Table 当年设计成 数组结构,以数组的下标作为虚页号是经过考虑的,即使今天看来,我们依然能够体会它的巧妙。 内核在进入 start_kernel 前,将 esp 指向了 init_thread_union + THREAD_SIZE,
init_thread_union 位于 .data.init_task 节,是个 union 结构,其大小为
THREAD_SIZE,定义的比较巧妙: union thread_union { linux 下线程库的实现,以常用的 Linuxthread 或者 NPTL 为例,这些库创建线程是使用这些标志 CLONE_VM |
CLONE_FS | CLONE_FILES | CLONE_SIGHAND 调用 clone() 来实现的,就是一个轻量级进程。 #define alloc_thread_info(tsk) kmalloc(THREAD_SIZE, GFP_KERNEL) 分配了 THREAD_SIZE 的空间,底端为 thread_info ,指向它的指针保存于
task_struct->thread_info 中。可以看到,task_struct->thread_info +
THREAD_SIZE 即为进程或者轻量级进程的内核栈栈底。 无论是使用 clone 生成轻量级进程,还是使用 kernel_thread 创建内核线程,他们都会进入 do_fork,进而调用
alloc_thread_info(),因此他们都有内核栈,大小为 THREAD_SIZE,底端为 thread_info 。 因此linux 下,每个线程都会有一个内核栈,这个当线程数目很大时是个问题。 |
chinaunix网友2011-03-09 13:25:30
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com