linux下内存各数据分析,Linux内存分析(1) -- 高端内存初试化

第一节 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<

//一共有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 longhash_ptr(void *ptr,       unsigned int bits)

{

return     hash_long((unsigned long)ptr,      bits);

}

static inline unsigned longhash_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 */

#defineGOLDEN_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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值