您的位置:首页 > 运维架构 > Linux

Linux内存分析(1) -- 高端内存初试化

2012-03-26 22:18 232 查看

第一节 Start_kernel之前

要分析内存,先从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区域为空。

第二节 Start_kernel中的内存初始化

一、page_address_init()

该函数负责初始化高端内存,具体的文件实现在mm\highmem.c中。所以我们可以先跳进该文件去阅读他的代码。


小结:

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;

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: