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, 即40964bytes,从此处分配的页表空间包括了用户空间页表和内核空间页表。

用户空间页表映射了地址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的数值,所以实际上页表的项数实际应该为20482的4096个项数,然后每项为4个字节,最后与上文分配的16K地址对应起来,即40964bytes=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为用户空间的地址值,之后才是内核空间的。

作者:lieye_leaves
来源:CSDN
原文:https://blog.csdn.net/lieye_leaves/article/details/50809973
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/wdjjwb/article/details/87946683