深入理解Linux内存映射机制精品资料.doc
《深入理解Linux内存映射机制精品资料.doc》由会员分享,可在线阅读,更多相关《深入理解Linux内存映射机制精品资料.doc(55页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、深入理解Linux内存映射机制创建时间:2008-08-31文章属性:原创文章提交:wzt (wzt_at_xsec.org)深入理解Linux内存映射机制Author: wztEMail: wztxsec.orgSite: http:/www.xsec.org & Date: 2008-6-13一. 绪 论二. X86的硬件寻址方法三. 内核对页表的设置四. 实例分析映射机制一. 绪 论我们经常在程序的反汇编代码中看到一些类似0x32118965这样的地址,操作系统中称为线性地址,或虚拟地址。虚拟地址有什么用?虚拟地址又是如何转换为物理内存地址的呢?本章将对此作一个简要阐述。1.1Linux
2、内存寻址概述现代意义上的操作系统都处于32位保护模式下。每个进程一般都能寻址4G的物理空间。但是我们的物理内存一般都是几百M,进程怎么能获得4G的物理空间呢?这就是使用了虚拟地址的好处,通常我们使用一种叫做虚拟内存的技术来实现,因为可以使用硬盘中的一部分来当作内存使用。例外一点现在操作系统都划分为系统空间和用户空间,使用虚拟地址可以很好的保护内核空间被用户空间破坏。对于虚拟地址如何转为物理地址,这个转换过程有操作系统和CPU共同完成. 操作系统为CPU设置好页表。CPU通过MMU单元进行地址转换。1.2浏览内核代码的工具现在的内核都很大, 因此我们需要某种工具来阅读庞大的源代码体系,现在的内核
3、开发工具都选用vim+ctag+cscope浏览内核代码,网上已有现成的makefile文件用来生成ctags/cscope/etags。一、用法:找一个空目录,把附件Makefile拷贝进去。然后在该目录中选择性地运行如下make命令:$ make将处理/usr/src/linux下的源文件,在当前目录生成ctags, cscope 注:SRCDIR用来指定内核源代码目录,如果没有指定,则缺省为/usr/src/linux/1) 只创建ctags$ make SRCDIR=/usr/src/linux-2.6.12/ tags2) 只创建cscope$ make SRCDIR=/usr/sr
4、c/linux-2.6.12/ cscope3) 创建ctags和cscope$ make SRCDIR=/usr/src/linux-2.6.12/4) 只创建etags$ make SRCDIR=/usr/src/linux-2.6.12/ TAGS二、处理时包括的内核源文件:1) 不包括drivers,sound目录2) 不包括无关的体系结构目录3) fs目录只包括顶层目录和ext2,proc目录三、最简单的ctags命令1) 进入进入vim后,用:tag func_name跳到函数func_name2) 看函数(identifier)想进入光标所在的函数,用CTRL + 3) 回退回退
5、用CTRL + T1.3内核版本的选取本次论文分析, 我选取的是linux-2.6.10版本的内核。最新的内核代码为2.6.25。但是现在主流的服务器都使用的是RedHat AS4的机器,它使用2.6.9的内核。我选取2.6.10是因为它很接近2.6.9,现在红帽企业Linux 4以Linux2.6.9内核为基础,是最稳定、最强大的商业产品。在2004年期间,Fedora等开源项目为Linux 2.6内核技术的更加成熟提供了一个环境,这使得红帽企业 Linux v.4内核可以提供比以前版本更多更好的功能和算法,具体包括:? 通用的逻辑CPU调度程序:处理多内核和超线程CPU。? 基于对象的逆向
6、映射虚拟内存:提高了内存受限系统的性能。? 读复制更新:针对操作系统数据结构的SMP算法优化。? 多I/O调度程序:可根据应用环境进行选择。? 增强的SMP和NUMA支持:提高了大型服务器的性能和可扩展性。? 网络中断缓和(NAPI):提高了大流量网络的性能。Linux 2.6 内核使用了许多技术来改进对大量内存的使用,使得 Linux 比以往任何时候都更适用于企业。包括反向映射(reverse mapping)、使用更大的内存页、页表条目存储在高端内存中,以及更稳定的管理器。因此,我选取linux-2.6.10内核版本作为分析对象。二. X86的硬件寻址方法请参考Intel x86手册_三.
7、 内核对页表的设置CPU做出映射的前提是操作系统要为其准备好内核页表,而对于页表的设置,内核在系统启动的初期和系统初始化完成后都分别进行了设置。3.1 与内存映射相关的几个宏这几个宏把无符号整数转换成对应的类型#define _pte(x)(pte_t) (x) )#define _pmd(x) (pmd_t) (x) )#define _pgd(x)(pgd_t) (x) )#define _pgprot(x)(pgprot_t) (x) )根据x把它转换成对应的无符号整数#define pte_val(x)(x).pte_low)#define pmd_val(x) (x).pmd)#de
8、fine pgd_val(x) (x).pgd)#define pgprot_val(x) (x).pgprot)把内核空间的线性地址转换为物理地址#define _pa(x)(unsigned long)(x)-PAGE_OFFSET)把物理地址转化为线性地址#define _va(x) (void *)(unsigned long)(x)+PAGE_OFFSET)x是页表项值, 通过pte_pfn得到其对应的物理页框号, 最后通过pfn_to_page得到对应的物理页描述符#define pte_page(x) pfn_to_page(pte_pfn(x)如果对应的表项值为0, 返回1#d
9、efine pte_none(x) (!(x).pte_low)x是页表项值, 右移12位后得到其对应的物理页框号#define pte_pfn(x) (unsigned long)(x).pte_low PAGE_SHIFT)根据页框号和页表项的属性值合并成一个页表项值#define pfn_pte(pfn, prot) _pte(pfn) PAGE_SHIFT) | pgprot_val(prot)根据页框号和页表项的属性值合并成一个中间表项值#define pfn_pmd(pfn, prot) _pmd(pfn) PGDIR_SHIFT) & (PTRS_PER_PGD-1)根据页描述符
10、和属性得到一个页表项值#define mk_pte(page, pgprot)pfn_pte(page_to_pfn(page), (pgprot)3.2内核页表的初始化内核在进入保护模式前, 还没有启用分页功能, 在这之前内核要先建立一个临时内核页表,因为在进入保护模式后, 内核继续初始化直到建立完整的内存映射机制之前, 仍然需要用到页表来映射相应的内存地址。 临时页表的初始化是在arch/i386/kernel/head.S中进行的:swapper_pg_dir是临时页全局目录表, 它是在内核编译过程中静态初始化的.pg0是第一个页表开始的地方, 它也是内核编译过程中静态初始化的.内核通过
11、以下代码建立临时页表:ENTRY(startup_32)/* 得到开始目录项的索引,从这可以看出内核是在swapper_pg_dir的768个表项开始进行建立的, 其对应的线性地址就是0xc0000000以上的地址, 也就是内核在初始化它自己的页表 */page_pde_offset = (_PAGE_OFFSET 20);/* pg0地址在内核编译的时候, 已经是加上0xc0000000了, 减去0xc00000000得到对应的物理地址 */movl $(pg0 - _PAGE_OFFSET), %edi/* 将目录表的地址传给edx, 表明内核也要从0x00000000开始建立页表, 这样
12、可以保证从以物理地址取指令到以线性地址在系统空间取指令的平稳过渡, 下面会详细解释 */movl $(swapper_pg_dir - _PAGE_OFFSET), %edx movl $0x007, %eax leal 0x007(%edi),%ecxMovl %ecx,(%edx)movl %ecx,page_pde_offset(%edx)addl $4,%edxmovl $1024, %ecx11:stosladdl $0x1000,%eaxloop 11b/* 内核到底要建立多少页表, 也就是要映射多少内存空间, 取决于这个判断条件。在内核初始化程中内核只要保证能映射到包括内核的代码
13、段,数据段, 初始页表和用于存放动态数据结构的128k大小的空间就行 */leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebpcmpl %ebp,%eaxjb 10bmovl %edi,(init_pg_tables_end - _PAGE_OFFSET)在上述代码中, 内核为什么要把用户空间和内核空间的前几个目录项映射到相同的页表中去呢,虽然在head.S中内核已经进入保护模式,但是内核现在是处于保护模式的段式寻址方式下,因为内核还没有启用分页映射机制,现在都是以物理地址来取指令, 如果代码中遇到了符号地址,只能减去0xc0000000才行, 当开启了映射机
14、制后就不用了现在cpu中的取指令指针eip仍指向低区,如果只建立内核空间中的映射, 那么当内核开启映射机制后, 低区中的地址就没办法寻址了,应为没有对应的页表, 除非遇到某个符号地址作为绝对转移或调用子程序为止。因此要尽快开启CPU的页式映射机制.movl $swapper_pg_dir-_PAGE_OFFSET,%eaxmovl %eax,%cr3/* cr3控制寄存器保存的是目录表地址 */movl %cr0,%eax/* 向cr0的最高位置1来开启映射机制 */orl $0x80000000,%eaxmovl %eax,%cr0ljmp $_BOOT_CS,$1f /* Clear pr
15、efetch and normalize %eip */1:lss stack_start,%esp通过ljmp $_BOOT_CS,$1f这条指令使CPU进入了系统空间继续执行 因为_BOOT_CS是个符号地址,地址在0xc0000000以上。在head.S完成了内核临时页表的建立后,它继续进行初始化,包括初始化INIT_TASK,也就是系统开启后的第一个进程;建立完整的中断处理程序,然后重新加载GDT描述符,最后跳转到init/main.c中的start_kernel函数继续初始化.3.3内核页表的完整建立内核在start_kernel()中继续做第二阶段的初始化,因为在这个阶段中, 内核
16、已经处于保护模式下,前面只是简单的设置了内核页表, 内核必须首先要建立一个完整的页表才能继续运行,因为内存寻址是内核继续运行的前提。pagetable_init()的代码在mm/init.c中:start_kernel()setup_arch()paging_init()pagetable_init()为了简单起见, 我忽略了对PAE选项的支持。static void _init pagetable_init (void) pgd_t *pgd_base = swapper_pg_dir;kernel_physical_mapping_init(pgd_base);在这个函数中pgd_base
17、变量指向了swapper_pg_dir, 这正是内核目录表的开始地址,pagetable_init()函数在通过kernel_physical_mapping_init()函数完成内核页表的完整建立。kernel_physical_mapping_init函数同样在mm/init.c中, 我略去了与PAE模式相关的代码:static void _init kernel_physical_mapping_init(pgd_t *pgd_base)unsigned long pfn;pgd_t *pgd;pmd_t *pmd;pte_t *pte;int pgd_idx, pmd_idx, pte
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 深入理解Linux内存映射机制 精品资料 深入 理解 Linux 内存 映射 机制 精品 资料
限制150内