设为首页 加入收藏

TOP

Linux-3.14.12内存管理笔记【建立内核页表(1)】(一)
2019-09-30 16:44:21 】 浏览:84
Tags:Linux-3.14.12 内存 管理 笔记 建立 内核

前面已经分析过了Intel的内存映射和linux的基本使用情况,已知head_32.S仅是建立临时页表,内核还是要建立内核页表,做到全面映射的。下面就基于RAM大于896MB,而小于4GB ,切CONFIG_HIGHMEM配置了高端内存的环境情况进行分析。

建立内核页表前奏,了解两个很关键的变量:

  • max_pfn:最大物理内存页面帧号;
  • max_low_pfn:低端内存区(直接映射空间区的内存)的最大可用页帧号;

max_pfn 的值来自setup_arch()中,setup_arch()函数中有:

max_pfn = e820_end_of_ram_pfn();

那么接下来看一下e820_end_of_ram_pfn()的实现:

804762

【file:/arch/x86/kernel/e820.c】
unsigned long __init e820_end_of_ram_pfn(void)
{
    return e820_end_pfn(MAX_ARCH_PFN, E820_RAM);
}

e820_end_of_ram_pfn()直接封装调用e820_end_pfn(),而其入参为MAX_ARCH_PFN和E820_RAM,其中MAX_ARCH_PFN的定义(x86的32bit环境)为:

#  define MAX_ARCH_PFN              (1ULL<<(32-PAGE_SHIFT))

最终值为0x100000,它表示的是4G物理内存的最大页面帧号;而E820_RAM为:

#define E820_RAM                 1

接下来看一下e820_end_pfn()函数实现:

【file:/arch/x86/kernel/e820.c】
/*
 * Find the highest page frame number we have available
 */
static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
{
    int i;
    unsigned long last_pfn = 0;
    unsigned long max_arch_pfn = MAX_ARCH_PFN;
 
    for (i = 0; i < e820.nr_map; i++) {
        struct e820entry *ei = &e820.map[i];
        unsigned long start_pfn;
        unsigned long end_pfn;
 
        if (ei->type != type)
            continue;
 
        start_pfn = ei->addr >> PAGE_SHIFT;
        end_pfn = (ei->addr + ei->size) >> PAGE_SHIFT;
 
        if (start_pfn >= limit_pfn)
            continue;
        if (end_pfn > limit_pfn) {
            last_pfn = limit_pfn;
            break;
        }
        if (end_pfn > last_pfn)
            last_pfn = end_pfn;
    }
 
    if (last_pfn > max_arch_pfn)
        last_pfn = max_arch_pfn;
 
    printk(KERN_INFO "e820: last_pfn = %#lx max_arch_pfn = %#lx\n",
             last_pfn, max_arch_pfn);
    return last_pfn;
}

这个函数用来查找最大物理的页面帧号,通过对e820图的内存块信息得到内存块的起始地址,将起始地址右移PAGE_SHIFT,算出其起始地址对应的页面帧号,如果足够大,超出了limit_pfn则设置最大页面帧号为limit_pfn,否则则设置为遍历中找到的最大的last_pfn。

e820_end_of_ram_pfn()函数的调用位置:

start_kernel()                           #init/main.c

└─>setup_arch()                        #arch/x86/kernel/setup.c

├─>e820_end_of_ram_pfn()              #arch/x86/kernel/e820.c

└─>find_low_pfn_range()               #arch/x86/kernel/e820.c

其中find_low_pfn_range()用于查找低端内存的最大页面数的 ,max_low_pfn则在这里面初始化。

find_low_pfn_range()代码实现:

【file:/arch/x86/mm/init_32.c】
/*
 * Determine low and high memory ranges:
 */
void __init find_low_pfn_range(void)
{
    /* it could update max_pfn */
 
    if (max_pfn <= MAXMEM_PFN)
        lowmem_pfn_init();
    else
        highmem_pfn_init();
}

函数实现很简单,根据max_pfn是否大于MAXMEM_PFN,从而判断是否初始化高端内存,也可以认为是启用。那么来看一下MAXMEM_PFN的宏定义:

(file:/arch/x86/include/asm/setup.h)

#define MAXMEM_PFN               PFN_DOWN(MAXMEM)

其中PFN_DOWN(x)的定义为:

(file:/include/linux/pfn.h)

#define PFN_DOWN(x)              ((x) >> PAGE_SHIFT)

PFN_DOWN(x)是用来返回小于x的最后一个页面号,对应的还有个PFN_UP(x)是用来返回大于x的第一个页面号,此外有个PFN_PHYS(x)返回的是x的物理页面号。接着看MAXMEM的定义:

(file:arch/x86/include/asm/pgtable_32_types.h)

#define MAXMEM                   (VMALLOC_END - PAGE_OFFSET - __VMALLOC_RESERVE)

那么VMALLOC_END的定义则为:

(file:arch/x86/include/asm/pgtable_32_types.h)

#define VMALLOC_END              (PKMAP_BASE - 2 * PAGE_SIZE)

//永久内存映射
#define PKMAP_BASE ((FIXADDR_BOOT_START - PAGE_SIZE * (LAST_PKMAP + 1)) & PMD_MASK)

其中PKMAP_BASE是永久映射空间的起始地址,LAST_PKMAP则是永久映射空间的映射页面数,定义为:

#define LAST_PKMAP 1024

另外PAGE_SHIFT和PAGE_SIZE的定义为:

#define PAGE_SHIFT               12
#define PAGE_SIZE                (_AC(1,UL) << PAGE_SHIFT)

而FIXADDR_BOOT_START是临时固定映射空间起始地址,其的相关宏定义:

临时内存映射:

#define FIXADDR_BOOT_SIZE        (__end_of_fixed_address
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇嵌入式02 STM32 实验05 蜂鸣器 下一篇USART_FLAG_TXE和USART_FLAG_TC

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目