linux内核系统讲解(带你了解Linux内核页表)

linux内核页表

一. Linux地址空间

ARM的32位系统共支持4G的内存空间,其中0-3G为用户空间,3G-4G是内核空间,

ARM采用2级页表,32位地址空间ADDRESS分别为 PGD|PTE|12Bits, 在内核代码中分别为PGD 11位,PTE 9 位,页内地址12位;但是在MMU系统中对于ARM的二级分页设置分别为PGD 12位,PTE 8位,页内地址为12位。在内核代码层次虽然是11位,但是经过代码中的设置,最后都映射到MMU起作用时的PGD12位,PTE8位。看代码定义。

#define PTRS_PER_PGD 2048 //PGD页中的指针数#definePGDIR_SHIFT 21 //地址中偏移位数,去前11位,因为要右移21位#define PGDIR_SIZE (1UL<< PGDIR_SHIFT) 0x20 0000#define USER_PTRS_PER_PGD (TASK_SIZE/ PGDIR_SIZE)在linux-3.5中,用户空间大小TASK_SIZE定义如下#define TASK_SIZE (UL(CONFIG_PAGE_OFFSET)- UL(0x01000000))#define CONFIG_PAGE_OFFSET 0xC000 0000在linux-2.6.25中,用户空间大小TASK_SIZE定义#define TASK_SIZE 0xC000 0000

二. 页表

页表分为用户空间页表和内核空间页表,不同的进程,它用户空间是不同的,所以它的用户空间页表是不同的,但是不同的进程它的内核空间是共享的,它的内核空间页表也是相同的。

在创建一个进程时,会为它创建一个页表指针,即mm_struct数据结构中的pgd_t * pgd;分配的函数是mm_init ()->mm_alloc_pgd(struct mm_struct *mm) ->pgd_alloc(structmm_struct *mm) ->#define __pgd_alloc() (pgd_t*)__get_free_pages(GFP_KERNEL, 2),分配的空间为16k, 即4096*4bytes,从此处分配的页表空间包括了用户空间页表和内核空间页表。

用户空间页表映射了地址0x0000 0000到0xC000 0000的空间,即为TASK_SIZE的大小。然后

#define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE)=0xC0000000/0x20 0000 =0x600=1536

内核空间的页表映射了地址0xC000 0000到0Xffff ffff的空间,为1G,然后

0x40000000/PGDIR_SIZE=0x4000 0000/0x20 0000 = 512,

所以页表的项数共为1536 512 =2048项。

考虑到硬件的地址映射,pgd实际为12位,即只需要右移20位,而不是21位,PGDIR_SHIFT的数值,所以实际上页表的项数实际应该为2048*2的4096个项数,然后每项为4个字节,最后与上文分配的16K地址对应起来,即4096*4bytes=16k.

三. 引导代码的内核页表分配

Linux-2.6的S3C2410上,当系统启动的时候,内核页表的地址是0x3000 4000, 内核的加载地址是0x3000 8000,所以内核页表的最大空间是0x4000,4字节,也为16K。

系统启动的时候,最先执行的是最后会成为空闲进程init_mm(), 该进程在启动结束前会生成内核进程init进程,然后再启动其他进程。在init_mm()结构中定义了.pgd = swapper_pg_dir,

#define CONFIG_PAGE_OFFSET 0xC000 0000#definePAGE_OFFSET UL(CONFIG_PAGE_OFFSET)#defineKERNEL_RAM_VADDR (PAGE_OFFSET TEXT_OFFSET).equ swapper_pg_dir,KERNEL_RAM_VADDR - PG_DIR_SIZE#definePG_DIR_SIZE 0x4000

空闲进程的页表初始化在引导代码的汇编中实现,__create_page_tables

四. 进程内核空间页表的分配

上面已经讨论了进程页表分为用户空间页表和内核空间页表,当进程创建的时候会把

new_pgd = __pgd_alloc(); if (!new_pgd) goto no_pgd; memset(new_pgd, 0, USER_PTRS_PER_PGD *sizeof(pgd_t)); /* * Copy over the kernel and IO PGD entries */ init_pgd = pgd_offset_k(0); memcpy(new_pgd USER_PTRS_PER_PGD,init_pgd USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) *sizeof(pgd_t)); // 以下是对pgd_offset_k(0)的解析,最后init_pgd即为swapper_pg_dir的值 #definepgd_index(addr) ((addr)>> PGDIR_SHIFT) #definepgd_offset(mm, addr) ((mm)->pgd pgd_index(addr)) #definepgd_offset_k(addr) pgd_offset(&init_mm,addr) / Memcpy()函数中参数的解析 #definePTRS_PER_PGD 2048,总的页数(暂时这么说)。 #defineUSER_PTRS_PER_PGD (TASK_SIZE /PGDIR_SIZE) 1536

前1536为用户空间的地址值,之后才是内核空间的。

五.11位和12位的问题

#define PGDIR_SHIFT 21 //地址中偏移位数,去前11位,因为要右移21位

#define PGDIR_SIZE (1UL<< PGDIR_SHIFT) 0x20 0000

ARM二级页表中PGD的位数为12, 即只要左移20为就行了,但是代码中却移动了21为,即多除了一个2;软件中相当于11位,那就可以这样理解,11位 1位,最后在11位的基础上补上0和1两个值来补齐12位,这样软件与硬件就对应起来。这样,前文中PGD页表的运算是根据11位来运算的,多除了一个2,现在补上0或者1后,它的数值应该翻倍,所以总的页表项数,应该为:

用户空间页表映射了地址0x0000 0000到0xC000 0000的空间,即为TASK_SIZE的大小。然后

#define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE)=0xC0000000/0x20 0000 =0x600=1536 // 1536*2 = 3072

内核空间的页表映射了地址0xC000 0000到0Xffff ffff的空间,为1G,然后

0x40000000/PGDIR_SIZE=0x4000 0000/0x20 0000 = 512, // 512*2 = 1024

所以页表的项数共为 1536 512=2048项。 //2048*2 =4096

然后每项为4字节, 4096*4 = 16K , 即为每个进程分配的PGD的页表空间大小,这样就对应上了。

补充:

实际上,内核代码一级页表只用到了16K/2,即8K。每项11bit, 但是硬件是12bit,所以一级页表中每项指向两个pte,最低位是即没有的12bit是0或者1。硬件中中每个pte实际上是8bit,即256项,软件上9bit,512项;所以在每项11位的pgd上就指向了两个pte, 空间大小是256*2=512,每项4字节,即2048byte,内核在分配的时候是分配了4K,即4096byte,多余的2048byte对应硬件不支持的dirty等选项。

static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, pmdval_t prot){undefined pmdval_t pmdval = (pte PTE_HWTABLE_OFF) | prot; pmdp[0] = __pmd(pmdval); #ifndef CONFIG_ARM_LPAE pmdp[1] = __pmd(pmdval 256 * sizeof(pte_t)); #endif flush_pmd_entry(pmdp);}

六.内核空间页表的分配(以linux-3.5为例)

在初始化内核空间页表时,大部分的内核空间已经映射好了,如内核代码的地址,内核内存地址,影响内核页表的内存分配主要有永久内核映射,临时内核映射,非连续内存分配等,当分配内存的时候,内核更新swapper_pg_dir地址下的页表,当访问该地址时,就会产生取值异常,然后就内核就把相应的内核映射的对应项复制到进程的相应的内核页表项中。

6.1 内核空间地址内存的分配

在系统初始化的时候,内核就会初始化内核内存的内核空间页表,

void __init paging_init(struct machine_desc *mdesc){undefined……map_lowmem();--àcreate_mapping(&map,false);……}

6.2 永久内核映射

pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB) kmap() {undefined might_sleep(); if(!PageHighMem(page)) returnpage_address(page); returnkmap_high(page); } #define PKMAP_BASE (PAGE_OFFSET - PMD_SIZE) //0xbfe00000 #define PAGE_OFFSET 0xC000 0000 #define PMD_SHIFT 21 #define PMD_SIZE (1UL << PMD_SHIFT) #define LAST_PKMAP PTRS_PER_PTE //512 #define LAST_PKMAP_MASK (LAST_PKMAP - 1) //511 last_pkmap_nr = (last_pkmap_nr 1) &LAST_PKMAP_MASK;

512 * 4k = 2 M,如标题所示。

6.3临时内核映射

ixmap : 0xfff00000 - 0xfffe0000 ( 896kB) void *kmap_atomic(struct page *page) 4K一个页面 #define FIXADDR_START 0xfff00000UL #define FIXADDR_TOP 0xfffe0000UL #define FIXADDR_SIZE (FIXADDR_TOP - FIXADDR_START) #define FIX_KMAP_BEGIN 0 #define FIX_KMAP_END (FIXADDR_SIZE >> PAGE_SHIFT)

6.4非连续内存分配

vmalloc : 0xe4800000 - 0xfc000000 ( 376 MB) vmalloc() #define VMALLOC_START (((unsignedlong)high_memory VMALLOC_OFFSET) & ~ (VMALLOC_OFFSET-1)) #define VMALLOC_END 0xff000000UL high_memory = __va(arm_lowmem_limit - 1) 1; //此处定义有疑义; vmalloc -> __vmalloc_node_flags->__vmalloc_node -> __vmalloc_node_range -> __vmalloc_area_node -> (此处可能有递归但最终会调用此函数:) map_vm_area -> vmap_page_range ->vmap_page_range_noflush -> vmap_pud_range -> vmap_pmd_range -> vmap_pte_range -> pte_alloc_kernel ->__pte_alloc_kernel -> pmd_populate_kernel (&init_mm, pmd, new)->__pmd_populate(pmdp, __pa(ptep), _PAGE_KERNEL_TABLE) static inline void __pmd_populate(pmd_t*pmdp, phys_addr_t pte,pmdval_t prot) {undefined pmdval_tpmdval = (pte PTE_HWTABLE_OFF) | prot; pmdp[0]= __pmd(pmdval); #ifndef CONFIG_ARM_LPAE pmdp[1]= __pmd(pmdval 256 * sizeof(pte_t)); #endif flush_pmd_entry(pmdp); }

七.空值异常

当内核访问某个地址发生异常时就会触发取值异常,从中断中进入函数

do_translation_fault(unsigned long addr,unsigned int fsr, struct pt_regs *regs){undefined if(addr < TASK_SIZE) returndo_page_fault(addr, fsr, regs); //用户空间的内存分配 如果地址大于TASK_SIZE,即 0xC000 0000,是内核空间的地址 进入内核空间页表的分配。}

最后小编自己整理了一些个人觉得比较好的学习视频文档资料有需要的可以私信【内核】自行免费领取哦!!

linux内核系统讲解(带你了解Linux内核页表)(1)

原文链接:https://blog.csdn.net/lieye_leaves/article/details/50809973

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页