要分析内存,先从uboot开始,关于uboot的TAG属性传递,可以参考第九章第一节。
随后进入内核启动的汇编代码部分(arch\arm\kernel\head.s),在检查完CPU型号和机器型号之后,便调用__create_page_tables函数进行一级页表的初始化设置。具体的代码分析见head.s中对应的代码。
这里需要说明的是
pgtbl r4
这个宏:
.macro pgtbl, rd
ldr \rd, =(KERNEL_RAM_PADDR - 0x4000) //16K
.endm
#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)
其中PHYS_OFFSET为RAM的物理开始地址
TEXT_OFFSET在makefile文件中定位为8000,即32K地址处。
启动linux后,16K的区域开始放一级页表。而0~16K区域为空。
小结:
1、 这个文件主要是初始化高端内存链表的,由于我们分析的ARM中没有起用高端内存,所以这个文件中的代码基本没意义。
2、 关于哈希表,实际就是将要查的值 *一个常数,然后取高N位,即对应了该节点在哈希表的位置,而至于这个常数,取值则与数学有关,这里不打算深究。
3、 关于管理高端内存所需要用到的一个全局链表为page_address_pool,即空闲链表,初始化时挂上了全部的永久地址映射数组page_address_maps[LAST_PKMAP];
4、 映射一块高端内存时,从page_address_pool中取出一个page_address_maps元素,设置好page和虚拟地址后,将其挂到哈系表page_address_htable数组中(lh成员下)。
5、 释放一块高端内存的操作与映射相反,根据page算出在哈希表page_address_htable的下标,然后遍历该下标对应的链表取出对应的page_address_map节点,将该节点返回给page_address_pool中
引出的函数:
EXPORT_SYMBOL(page_address)
void *page_address(struct page *page)
//返回page描述符所影射的虚地址 – page在管理页中,返回的是二级页表指的地址
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define PA_HASH_ORDER 7 //哈希表长度为7
struct page_address_map {
struct page *page;
void *virtual;
struct list_head list; //链表节点,挂载在page_address_slot结构体中
};
static struct list_head page_address_pool; //空闲链表
static spinlock_t pool_lock; //空闲链表的锁
/*
* Hash table bucket
*/
static struct page_address_slot {
struct list_head lh; //page_address_map的宿主链表
spinlock_t lock; //锁
} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
//一共有128个哈希链表
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//start_kernel中调用
static struct page_address_map page_address_maps[LAST_PKMAP];
//通过各方资料查知LAST_PKMAP为1K,用于永久地址映射
void __init page_address_init(void)
{
int i;
INIT_LIST_HEAD(&page_address_pool); //空闲链表挂空
//将节点挂到空闲链表上
for (i = 0; i < ARRAY_SIZE(page_address_maps); i++)
list_add(&page_address_maps[i].list, &page_address_pool);
for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
INIT_LIST_HEAD(&page_address_htable[i].lh); //挂空宿主链表
spin_lock_init(&page_address_htable[i].lock);
}
spin_lock_init(&pool_lock); //初始化空闲链表锁
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//根据page获取该page地址所在的哈希列表表头(从128个表中找到page所在是哪一个)
static struct page_address_slot *page_slot(struct page *page)
{
// PA_HASH_ORDER为7
//利用哈希算法得到数组下表,然后返回该页(page)所在的哈希链表的表头
return &page_address_htable[hash_ptr(page, PA_HASH_ORDER)];
}
//hash_ptr是根据ptr地址算出该地址在哈希表中的索引的,具体的计算公式为:
(value *常数) >> 25
//关于常数的取值,不可考,要深究得研究到复杂的数学算法里去了。具体的哈希表介绍可以参考以下网址的介绍,赶时间的朋友只看第二部分就可以了。
http://blog.csdn.net/v_JULY_v/article/details/6256463
static inline unsigned long hash_ptr(void *ptr, unsigned int bits)
{
return hash_long((unsigned long)ptr, bits);
}
static inline unsigned long hash_long(unsigned long val, unsigned int bits)
{
unsigned long hash = val;
hash *= GOLDEN_RATIO_PRIME;
return hash >> (BITS_PER_LONG - bits); //BIT = 32,这里只取高7位
}
/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
#define GOLDEN_RATIO_PRIME 0x9e370001UL //哈希乘数
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//返回page描述符所影射的虚地址
void *page_address(struct page *page)
{
unsigned long flags;
void *ret;
struct page_address_slot *pas;
if (!PageHighMem(page)) //返回0
return lowmem_page_address(page); //返回page描述符所映射的虚拟地址
static __always_inline void *lowmem_page_address(struct page *page)
{
//通过page_to_pfn获取page在mem_map中的位置
//然后乘以页大小得到物理地址
//在__va计算出虚拟地址
return __va(page_to_pfn(page) << PAGE_SHIFT);
}
}
EXPORT_SYMBOL(page_address);
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//从哈希表中添加/删除page对应的内存
void set_page_address(struct page *page, void *virtual)
{
unsigned long flags;
struct page_address_slot *pas;
struct page_address_map *pam;
//返回0,出错(page没有映射到虚拟地址)
//由于没有配备高端内存,所以这个函数实际是不能调用的
BUG_ON(!PageHighMem(page));
pas = page_slot(page); //根据page获取page所在哈希表的表头
//从一段的分析可以看出,page_address_pool是一个空闲链表
添加时从表头取一块地址,写入数据后加进pas->lh中。
删除时从pas->lh中把数据取出来,恢复进page_address_pool
if (virtual) { //虚拟地址不为0,添加
BUG_ON(list_empty(&page_address_pool)); // page_address_pool为空,出错
spin_lock_irqsave(&pool_lock, flags); //禁止中断
pam = list_entry(page_address_pool.next,
struct page_address_map,
list); //获取第一个元素
list_del(&pam->list); //删除
spin_unlock_irqrestore(&pool_lock, flags); //恢复中断
pam->page = page;
pam->virtual = virtual; //加入page和virtual
spin_lock_irqsave(&pas->lock, flags);
list_add_tail(&pam->list, &pas->lh); //添加到pas->lh中(page地址对应的哈希)
spin_unlock_irqrestore(&pas->lock, flags);
} else { /* Remove */
spin_lock_irqsave(&pas->lock, flags);
list_for_each_entry(pam, &pas->lh, list) {
if (pam->page == page) { //找到page对应的项
list_del(&pam->list); //将pam从链表中删除
spin_unlock_irqrestore(&pas->lock, flags);
spin_lock_irqsave(&pool_lock, flags);
list_add_tail(&pam->list, &page_address_pool); //将pam恢复进空闲表表头
spin_unlock_irqrestore(&pool_lock, flags);
goto done;
}
}
spin_unlock_irqrestore(&pas->lock, flags);
}
done:
return;
}