page_address()函数分析
由于X86平台上面,内存是划分为低端内存和高端内存的,所以在两个区域内的page查找对应的虚拟地址是不一样的。
一. x86上关于page_address()函数的定义
在include/linux/mm.h里面,有对page_address()函数的三种宏定义,主要依赖于不同的平台:
首先来看看几个宏的定义:
CONFIG_HIGHMEM:顾名思义,就是是否支持高端内存,可以查看config文件,一般推荐内存超过896M的时候,才配置为支持高端内存。
WANT_PAGE_VIRTUAL:X86平台是没有定义的。
所以下面的HASHED_PAGE_VIRTUAL在支持高端内存的i386平台上是有定义的
|
1.//所以这里是假的,page_address()在i386上不是在这里定义的
#define page_address(page) ((page)->virtual)
|
2.//在没有配置CONFIG_HIGHMEM的i386平台上,page_address是在这里定义的
#define page_address(page) lowmem_page_address(page) #define set_page_address(page, address) do { } while(0) #define page_address_init() do { } while(0) #endif |
3.//所以支持高端内存的i386平台上,page_address()是在这里定义的
|
二. 在低端内存中的page对应的page_address()的实现
在没有配置CONFIG_HIGHMEM的i386平台上,page_address()是等同于lowmem_page_address
():
static __always_inline void *lowmem_page_address(struct page *page) #define page_to_pfn(page) ((unsigned long)((page) - mem_map) + \
|
我们知道,在小于896M(低端内存)的物理地址空间和3G--3G+896M的线性地址空间是一一对应映射的,所以我们只要知道page所对应的物理地址,就可以知道这个page对应的线性地址空间(pa+PAGE_OFFSET)。
那如何找一个page对应的物理地址呢?我们知道物理内存按照大小为(1<<PAGE_SHIFT)分为很多个页,每个这样的页就对应一个struct
page *
page结构,这些页描述结构存放在一个称之为mem_map的数组里面,而且是严格按照物理内存的顺序来存放的,也就是物理上的第一个页描述结构,作为mem_map数组的第一个元素,依次类推。所以,每个页描述结构(page)在数组mem_map里的位置在乘以页的大小,就可以得到该页的物理地址了。上面的代码就是依照这个原理来的:
page_to_pfn(page)函数就是得到每个page在mem_map里的位置,左移PAGE_SHIFT就是乘以页的大小,这就得到了该页的物理地址。这个物理地址加上个PAGE_OFFSET(3G)就得到了该page的线性地址了
在低端内存中(小于896M),通过页(struct page * page)取得虚拟地址就是这样转换的。
三. 在高端内存中的page对应的page_address()的实现:
在有配置CONFIG_HIGHMEM的i386平台上,page_address是在mm/highmem.c里面实现的:
unsigned long flags; void *ret; struct page_address_slot *pas;
if (!PageHighMem(page)) //判断是否属于高端内存,如果不是,那么就是属于低 存的,通过上面的方法可以直接找到 return lowmem_page_address(page);
pas = page_slot(page); //见下分析,pas指向page对应的page_address_map结构所在的链表表头
ret = NULL; spin_lock_irqsave(&pas->lock, flags); if (!list_empty(&pas->lh)) {
struct page_address_map *pam;
list_for_each_entry(pam, &pas->lh, list) { if (pam->page == page) {
ret = pam->virtual; goto done;
} } done: spin_unlock_irqrestore(&pas->lock, flags); return ret;
|
在高端内存中,由于不能通过像在低端内存中一样,直接通过物理地址加PAGE_OFFSET得到线性地址,所以引入了一个结构叫做
page_address_map结构,该结构保存有每个page(仅高端内存中的)和对应的虚拟地址,所有的高端内存中的这种映射都通过链表链接起来,这个结构是在高端内存映射的时候建立,并加入到链表中的。
|
又因为如果内存远远大于896M,那么高端内存中的page就比较多((内存-896M)/4K个页,假设页大小为4K),如果只用一个链表来表示,那么查找起来就比较耗时了,所以这里引入了HASH算法,采用多个链表,每个page通过一定的hash算法,对应到一个链表上,总够有128个链表:
|
PA_HASH_ORDER=7, 所以一共有1<<7(128)个链表,每一个page通过HASH算法后对应一个 page_address_htable链表, 然后再遍历这个链表来找到对应的PAGE和虚拟地址。
page通过HASH算法后对应一个 page_address_htable链表的代码如下:
|
hash_ptr(val, bits)函数在32位的机器上是一个很简单的hash算法,就是把val乘一个黄金值 GOLDEN_RATIO_PRIME_32,在把得到的结果(32位)取高 bits位 (这里就是7位)作为哈希表的索引
/* High bits are more random, so use them. */ |
这样pas = page_slot(page)执行过后,pas就指向该page对应的page_address_map结构所在的链表的表头。
然后再遍历这个链表,就可以找到对应的线性地址(如果存在的话),否则就返回NULL
list_for_each_entry(pam, &pas->lh, list) {
if (pam->page == page) {
ret = pam->virtual;
goto done;
}
}
page_address()函数分析的更多相关文章
- page_address()函数分析--如何通过page取得虚拟地址
由于X86平台上面,内存是划分为低端内存和高端内存的,所以在两个区域内的page查找对应的虚拟地址是不一样的. 一. x86上关于page_address()函数的定义 在include/linux/ ...
- split(),preg_split()与explode()函数分析与介
split(),preg_split()与explode()函数分析与介 发布时间:2013-06-01 18:32:45 来源:尔玉毕业设计 评论:0 点击:965 split()函数可以实 ...
- string函数分析
string函数分析string函数包含在string.c文件中,经常被C文件使用.1. strcpy函数原型: char* strcpy(char* str1,char* str2);函数功能: 把 ...
- start_amboot()函数分析
一.整体流程 start_amboot()函数是执行完start.S汇编文件后第一个C语言函数,完成的功能自然还是初始化的工作 . 1.全局变量指针r8设定,以及全局变量区清零 2.执行一些类初始化函 ...
- uboot的jumptable_init函数分析
一.函数说明 函数功能:安装系统函数指针 函数位置:common/exports.c 二.函数分析 void jumptable_init (void) { int i; gd->jt = (v ...
- Linux-0.11内核源代码分析系列:内存管理get_free_page()函数分析
Linux-0.11内存管理模块是源码中比較难以理解的部分,如今把笔者个人的理解发表 先发Linux-0.11内核内存管理get_free_page()函数分析 有时间再写其它函数或者文件的:) /* ...
- 31.QPainter-rotate()函数分析-文字旋转不倾斜,图片旋转实现等待
在上章和上上上章: 28.QT-QPainter介绍 30.QT-渐变之QLinearGradient. QConicalGradient.QRadialGradient 学习了QPainter基础绘 ...
- 如何验证一个地址可否使用—— MmIsAddressValid函数分析
又是一篇内核函数分析的博文,我个人觉得Windows的内核是最好的老师,当你想实现一个功能之前可以看看Windows内核是怎么做的,说不定就有灵感呢:) 首先看下官方的注释说明: /*++ Routi ...
- STM32F10X固件库函数——串口清状态位函数分析
STM32F10X固件库函数——串口清状态位函数分析 最近在测试串口热插拔功能的时候,意外发现STM32F10X的串口库函数中,清理串口状态位函数稍稍有点不解.下面是改函数的源码: /******** ...
随机推荐
- [asp.net入门]利用ADO.NET处理数据的简单之处
由于项目需要,要往数据库中导入一些历史数据,而这些历史数据都是线下人工记录的,所以有很多不规范的地方,比如:同一个公司的名称在不同的记录中可能相差那么几个字,而且每条数据不是每个字段都是完整的,等等诸 ...
- 2013年度最新最佳CSS网页设计实例
CSS在网页设计中扮演着举足轻重的角色,尤其是CSS3和HTML5的出现,让我们更加相信CSS能给我们带来无限的遐想和强有力的视觉冲击.今天我要给大家推荐几个最新CSS佳作,虽然本人没有设计头脑,但还 ...
- POJ 2488 A Knight's Journey
A Knight's Journey Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 29226 Accepted: 10 ...
- Oracle Agile PLM Web Services 的实现
Oracle 的产品Agile PLM内置了许多Web Services,其他系统可以通过Web Servcies实现对Agile PLM系统资源的访问.快速学会使用的方法,是去Oracle的官网下载 ...
- 移动立方体算法(Marching cubes algorithm)
百度百科: 医学图像三维重建的方法主要有两大类:一类是三维面绘制,另一类是三维体绘制.体绘制能够更真实地反映物体结构,但由于其运算量大,即使利用高性能的计算机也无法满足实际应用中交互操作的需要.因此, ...
- 微信小程序价值思考:手机端的CS-BS迁移
从很多特点来看,小程序都非常类似于网页:主要的业务逻辑在服务端.客户端无需安装应用程序.小程序的开发采用的HTML+JS+CSS技术等等.张小龙自己对小程序的定位也大概如此:无意做小程序分发平台,只是 ...
- Bootstrap3免费单页面模板-Shuffle
在线演示 本地下载 这是一款当前最热门的模板,单页面模板现在越来越时兴,它简洁的页面和每一次滑动都带来的全新视角.非常值得收藏和使用!
- TCP/IP协议族——IP工作原理及实例具体解释(上)
IP协议具体解释 本文主要介绍了IP服务特点,头部结构,IP分片知识,并用tcpdump抓取数据包.来观察IP数据报传送过程中IP的格式,以及分片的过程. IP头部信息:IP头部信息出如今每一个 ...
- Linux/Unix分配进程ID的方法以及源代码实现
在Linux/Unix系统中.每一个进程都有一个非负整型表示的唯一进程ID.尽管是唯一的.可是进程的ID能够重用.当一个进程终止后,其进程ID就能够再次使用了. 大多数Linux/Unix系统採用延迟 ...
- android 如何连真机测试
1. 设置android手机为USB调试模式.步骤: menu---> 设置 ---> 应用程序 ---> 开发 , 选择[USB调试] 2. 用USB连接手机和电脑,并确保成功.步 ...