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

原创 2012年03月26日 22:18:00

第一节 Start_kernel之前

要分析内存,先从uboot开始,关于ubootTAG属性传递,可以参考第九章第一节。

随后进入内核启动的汇编代码部分(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_OFFSETRAM的物理开始地址

TEXT_OFFSETmakefile文件中定位为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_PKMAP1K,用于永久地址映射

 

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_ORDER7

      //利用哈希算法得到数组下表,然后返回该页(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获取pagemem_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;               //加入pagevirtual

 

              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;

}

 

相关文章推荐

kworker内核工作队列详解

工作队列是另一种将工作推后执行的形式,它可以把工作交给一个内核线程去执行,这个下半部是在进程上下文中执行的,因此,它可以重新调度还有睡眠。     区分使用软中断/tasklet还是工作队列比较简单...

Linux 3.x 内核学习笔记——x86 64位内存管理

地址映射 64位地址采用4层地址映射,如下图: pgd、pud、pmd、pte各占了9位,加上12位的页内index,共用了48位。即可管理的地址空间为2^48=256T。而在32位地址模式时,该...
  • liminyu
  • liminyu
  • 2013年10月09日 18:17
  • 7636

Linux启动中setup_arch分析

[ 注:内核版本Linux-2.6.30 ] setup_arch执行是由start_kernel来调用的: start_kernel [ init/main.c ] --> setup_arch...

linux arm的高端内存映射(1) vmalloc

高端内存映射   与高端映射对立的是低端映射或所谓直接映射,内核中有关变量定义它们的它们的分界点,全局变量high_memory,该变量定义在mm/memory.c文件中(存在MMU的前提下),可...

Linux内核初始化高端内存的过程(代码分析)

Linux内核初始化高端内存的过程            内核在start_kernel()函数中调用了mem_init()来做所有与内存初始化相关的工作。与初始化高端内存相关的工作在函数set_...
  • arethe
  • arethe
  • 2011年11月28日 17:56
  • 1279

Linux高端内存的由来

抱着拿来主义,自己挑选了部分,以下内容摘自网络。Linux内核地址空间划分 通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间。注意这里是32位内核地址空间划分,64位内核地址...

【Linux基础系列之】内存管理(2)-高端内存

常用内存分配和高端内存分配使用

linux 用户空间与内核空间——高端内存详解

Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数...

linux 用户空间与内核空间——高端内存详解

摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对...

linux用户空间与内核空间—高端内存详解

http://www.cnblogs.com/zlcxbb/p/5841417.html 摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux内存分析(1) -- 高端内存初试化
举报原因:
原因补充:

(最多只允许输入30个字)