![](/icons/45916yi.gif)
. 绪 论
2. X86
![](/icons/45916de.gif)
硬件寻址思路方法
3. 内核对页表
![](/icons/45916de.gif)
设置
4. 例子分析映射机制
![](/icons/45916yi.gif)
. 绪 论
我们经常在
![](/icons/45916chengxu.gif)
![](/icons/45916de.gif)
反汇编代码中看到
![](/icons/45916yi.gif)
些类似0x32118965这样
![](/icons/45916de.gif)
地址
![](/icons/45916dou.gif)
操作系统中称为线性地址
![](/icons/45916dou.gif)
或虚拟地址
![](/icons/45916dou2.gif)
虚拟地址有什么用?虚拟地址又是如何转换为物理内存地址
![](/icons/45916de.gif)
呢?本章将对此作
![](/icons/45916yi.gif)
个简要阐述
1.1 Linux内存寻址概述
现代意义上
![](/icons/45916de.gif)
操作系统都处于32位保护模式下
![](/icons/45916dou2.gif)
每个进程
![](/icons/45916yi.gif)
般都能寻址4G
![](/icons/45916de.gif)
物理空间
![](/icons/45916dou2.gif)
但是我们
![](/icons/45916de.gif)
物理内存
![](/icons/45916yi.gif)
般都是几百M
![](/icons/45916dou.gif)
进程如何能获得4G
![](/icons/45916de.gif)
物理空间呢?这就是使用了虚拟地址
![](/icons/45916de.gif)
好处
![](/icons/45916dou.gif)
通常我们使用
![](/icons/45916yi.gif)
种叫做虚拟内存
![](/icons/45916de.gif)
技术来实现
![](/icons/45916dou.gif)
![](/icons/45916yinwei.gif)
可以使用硬盘中
![](/icons/45916de.gif)
![](/icons/45916yi.gif)
部分来当作内存使用
![](/icons/45916dou2.gif)
例外
![](/icons/45916yi.gif)
点现在操作系统都划分为系统空间和用户空间
![](/icons/45916dou.gif)
使用虚拟地址可以很好
![](/icons/45916de.gif)
保护内核空间被用户空间破坏
对于虚拟地址如何转为物理地址,这个转换过程有操作系统和CPU共同完成. 操作系统为CPU设置好页表
![](/icons/45916dou2.gif)
CPU通过MMU单元进行地址转换
1.2 浏览内核代码
![](/icons/45916de.gif)
工具
现在
![](/icons/45916de.gif)
内核都很大
![](/icons/45916dou.gif)
因此我们需要某种工具来阅读庞大
![](/icons/45916de.gif)
源代码体系
![](/icons/45916dou.gif)
现在
![](/icons/45916de.gif)
内核开发工具都选用vim ctag cscope浏览内核代码
![](/icons/45916dou.gif)
网上已有现成
![](/icons/45916de.gif)
makefile文件用来生成ctags/cscope/etags
![](/icons/45916yi.gif)
、使用方法:
找
![](/icons/45916yi.gif)
个空目录
![](/icons/45916dou.gif)
把附件Makefile拷贝进去
![](/icons/45916dou2.gif)
然后在该目录中选择性地运行如下make命令:
$ make
将处理/usr/src/linux下
![](/icons/45916de.gif)
源文件
![](/icons/45916dou.gif)
在当前目录生成ctags, cscope
注:SRCDIR用来指定内核源代码目录
![](/icons/45916dou.gif)
如果没有指定
![](/icons/45916dou.gif)
则缺省为/usr/src/linux/
1) 只创建ctags
$ make SRCDIR=/usr/src/linux-2.6.12/ tags
2) 只创建cscope
$ make SRCDIR=/usr/src/linux-2.6.12/ cscope
3) 创建ctags和cscope
$ make SRCDIR=/usr/src/linux-2.6.12/
4) 只创建etags
$ make SRCDIR=/usr/src/linux-2.6.12/ TAGS
2、处理时包括
![](/icons/45916de.gif)
内核源文件:
1) 不包括drivers
![](/icons/45916dou.gif)
sound目录
2) 不包括无关
![](/icons/45916de.gif)
体系结构目录
3) fs目录只包括顶层目录和ext2
![](/icons/45916dou.gif)
proc目录
3、最简单
![](/icons/45916de.gif)
ctags命令
1) 进入
进入vim后
![](/icons/45916dou.gif)
用
:tag func_name
跳到
![](/icons/45916hanshu.gif)
func_name
2) 看
![](/icons/45916hanshu.gif)
(ident
![](/icons/45916if.gif)
ier)
想进入光标所在
![](/icons/45916de.gif)
![](/icons/45916hanshu.gif)
![](/icons/45916dou.gif)
用
CTRL ]
3) 回退
回退用 CTRL T
1.3 内核版本
![](/icons/45916de.gif)
选取
本次论文分析
![](/icons/45916dou.gif)
我选取
![](/icons/45916de.gif)
是linux-2.6.10版本
![](/icons/45916de.gif)
内核
![](/icons/45916dou2.gif)
最新
![](/icons/45916de.gif)
内核代码为2.6.25
![](/icons/45916dou2.gif)
但是现在主流
![](/icons/45916de.gif)
服务器都使用
![](/icons/45916de.gif)
是RedHat AS4
![](/icons/45916de.gif)
机器
![](/icons/45916dou.gif)
它使用2.6.9
![](/icons/45916de.gif)
内核
![](/icons/45916dou2.gif)
我选取2.6.10是
![](/icons/45916yinwei.gif)
它很接近2.6.9
![](/icons/45916dou.gif)
现在红帽企业Linux 4以Linux2.6.9内核为基础
![](/icons/45916dou.gif)
是最稳定、最强大
![](/icons/45916de.gif)
商业产品
![](/icons/45916dou2.gif)
在2004年期间
![](/icons/45916dou.gif)
Fedora等开源项目为Linux 2.6内核技术
![](/icons/45916de.gif)
更加成熟提供了
![](/icons/45916yi.gif)
个环境
![](/icons/45916dou.gif)
这使得红帽企业 Linux v.4内核可以提供比以前版本更多更好
功能和算法
![](/icons/45916dou.gif)
具体包括:
• 通用
![](/icons/45916de.gif)
逻辑CPU调度
![](/icons/45916chengxu.gif)
:处理多内核和超线程CPU
• 基于对象
![](/icons/45916de.gif)
逆向映射虚拟内存:提高了内存受限系统
![](/icons/45916de.gif)
性能
• 读复制更新:针对操作系统数据结构
![](/icons/45916de.gif)
SMP算法优化
• 多I/O调度
![](/icons/45916chengxu.gif)
:可根据应用环境进行选择
• 增强
![](/icons/45916de.gif)
SMP和NUMA支持:提高了大型服务器
![](/icons/45916de.gif)
性能和可扩展性
• 网络中断缓和(NAPI):提高了大流量网络
![](/icons/45916de.gif)
性能
Linux 2.6 内核使用了许多技术来改进对大量内存
![](/icons/45916de.gif)
使用
![](/icons/45916dou.gif)
使得 Linux 比以往任何时候都更适用于企业
![](/icons/45916dou2.gif)
包括反向映射(reverse mapping)、使用更大
![](/icons/45916de.gif)
内存页、页表条目存储在高端内存中
![](/icons/45916dou.gif)
以及更稳定
![](/icons/45916de.gif)
管理器
![](/icons/45916dou2.gif)
因此
![](/icons/45916dou.gif)
我选取linux-2.6.10内核版本作为分析对象
2. X86
![](/icons/45916de.gif)
硬件寻址思路方法
请参考Intel x86手册^_^
3. 内核对页表
![](/icons/45916de.gif)
设置
CPU做出映射
![](/icons/45916de.gif)
前提是操作系统要为其准备好内核页表
![](/icons/45916dou.gif)
而对于页表
![](/icons/45916de.gif)
设置
![](/icons/45916dou.gif)
内核在系统启动
![](/icons/45916de.gif)
初期和系统
![](/icons/45916chushi.gif)
化完成后都分别进行了设置
3.1 和内存映射相关
![](/icons/45916de.gif)
几个宏
这几个宏把无符号整数转换成对应
![](/icons/45916de.gif)
类型
#
![](/icons/45916define.gif)
__pte(x) ((pte_t) { (x) } )
#
![](/icons/45916define.gif)
__pmd(x) ((pmd_t) { (x) } )
#
![](/icons/45916define.gif)
__pgd(x) ((pgd_t) { (x) } )
#
![](/icons/45916define.gif)
__pgprot(x) ((pgprot_t) { (x) } )
根据x把它转换成对应
![](/icons/45916de.gif)
无符号整数
#
![](/icons/45916define.gif)
pte_val(x) ((x).pte_low)
#
![](/icons/45916define.gif)
pmd_val(x) ((x).pmd)
#
![](/icons/45916define.gif)
pgd_val(x) ((x).pgd)
#
![](/icons/45916define.gif)
pgprot_val(x) ((x).pgprot)
把内核空间
![](/icons/45916de.gif)
线性地址转换为物理地址
#
![](/icons/45916define.gif)
__pa(x) ((unsigned long)(x)-PAGE_OFFSET)
把物理地址转化为线性地址
#
![](/icons/45916define.gif)
__va(x) ((void *)((unsigned long)(x) PAGE_OFFSET))
x是页表项值
![](/icons/45916dou.gif)
通过pte_pfn得到其对应
![](/icons/45916de.gif)
物理页框号
![](/icons/45916dou.gif)
最后通过pfn_to_page得到对应
![](/icons/45916de.gif)
物理页描述符
#
![](/icons/45916define.gif)
pte_page(x) pfn_to_page(pte_pfn(x))
如果对应
![](/icons/45916de.gif)
表项值为0
![](/icons/45916dou.gif)
返回1
#
![](/icons/45916define.gif)
pte_none(x) (!(x).pte_low)
x是页表项值
![](/icons/45916dou.gif)
右移12位后得到其对应
![](/icons/45916de.gif)
物理页框号
#
![](/icons/45916define.gif)
pte_pfn(x) ((unsigned long)(((x).pte_low >> PAGE_SHIFT)))
根据页框号和页表项
![](/icons/45916de.gif)
属性值合并成
![](/icons/45916yi.gif)
个页表项值
#
![](/icons/45916define.gif)
pfn_pte(pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
根据页框号和页表项
![](/icons/45916de.gif)
属性值合并成
![](/icons/45916yi.gif)
个中间表项值
#
![](/icons/45916define.gif)
pfn_pmd(pfn, prot) __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
向
![](/icons/45916yi.gif)
个表项中写入指定
![](/icons/45916de.gif)
值
#
![](/icons/45916set.gif)
_pte(pteptr, pteval) (*(pteptr) = pteval)
#
![](/icons/45916set.gif)
_pte_atomic(pteptr, pteval)
![](/icons/45916set.gif)
_pte(pteptr,pteval)
#
![](/icons/45916set.gif)
_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
#
![](/icons/45916set.gif)
_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval)
根据线性地址得到高10位值
![](/icons/45916dou.gif)
也就是在目录表中
![](/icons/45916de.gif)
索引
#
![](/icons/45916define.gif)
pgd_index(address) (((address)>>PGDIR_SHIFT) & (PTRS_PER_PGD-1))
根据页描述符和属性得到
![](/icons/45916yi.gif)
个页表项值
#
![](/icons/45916define.gif)
mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), (pgprot))
3.2内核页表
![](/icons/45916de.gif)
![](/icons/45916chushi.gif)
化
内核在进入保护模式前
![](/icons/45916dou.gif)
还没有启用分页功能
![](/icons/45916dou.gif)
在这的前内核要先建立
![](/icons/45916yi.gif)
个临时内核
延伸阅读
最新评论