-
-
[原创]Linux高端内存映射之kmap持久内核映射
-
发表于:
2019-5-20 16:10
9154
-
[原创]Linux高端内存映射之kmap持久内核映射
在i386体系结构中,高于896MB的物理内存被划分为高端内存,这部分物理内存并不是永久性的映射到内核地址空间,而是根据内核的需求取合适大小的内存临时映射到内核地址空间。这里需要先澄清两个概念:
- 页框:物理内存划分为4096字节的等份,每一等份称作“页框”。
- 内核地址空间:段内偏移和段基址经过分段机构合成线性地址,i386体系中线性地址空间为4GB,其中0~3GB为用户地址空间,只能访问被标记为“用户”的页框,3~4GB为内核地址空间,可以访问所有页框。
如果物理内存超过1GB,那么内核地址空间是无法访问到所有内存的。为了解决这个问题,物理内存的0~896MB映射到内核地址空间0xc0000000~0xf8000000(3GB~3GB+896MB)。物理内存中超过896MB的部分则根据内核的需要,临时映射到内核地址空间剩下的128MB中。
如果物理内存超过1GB,那么内核地址空间是无法访问到所有内存的。为了解决这个问题,物理内存的0~896MB映射到内核地址空间0xc0000000~0xf8000000(3GB~3GB+896MB)。物理内存中超过896MB的部分则根据内核的需要,临时映射到内核地址空间剩下的128MB中。
3GB~4GB内核地址空间的分配如下:
3GB~3GB+896MB之间的内核空间直接映射到物理内存0~896MB,与物理地址的转换关系是:va=pa + 0xc0000000。
3GB~3GB+896MB之间的内核空间直接映射到物理内存0~896MB,与物理地址的转换关系是:va=pa + 0xc0000000。
由内核函数vmalloc分配,内核地址连续,但物理地址不一定连续,可能处于高端内存,也可能处于内存低端。
内核建立由高端内存页框到内核地址空间的长期映射。
留作特定用途。
由内核函数vmalloc分配,内核地址连续,但物理地址不一定连续,可能处于高端内存,也可能处于内存低端。
内核建立由高端内存页框到内核地址空间的长期映射。
留作特定用途。
页描述符,记录每个页框当前的状态,例如区分页框是属于内核还是用户进程,页框是否空闲。这些都保存在page类型的描述符中,所有描述符都保存在mem_map数组中。
页描述符,记录每个页框当前的状态,例如区分页框是属于内核还是用户进程,页框是否空闲。这些都保存在page类型的描述符中,所有描述符都保存在mem_map数组中。
散列表,用于建立高端内存页框与永久内核映射包含的线性地址之间的联系,由线性地址查找是否已有页框与之建立映射的时候可以提高查找速度。
散列表,用于建立高端内存页框与永久内核映射包含的线性地址之间的联系,由线性地址查找是否已有页框与之建立映射的时候可以提高查找速度。
page_address_htable包含的数据结构,用于为高端内存中每一个页框进行当前映射,包含一个指向页描述符的指针和分配给该页框的线性地址。
page_address_htable包含的数据结构,用于为高端内存中每一个页框进行当前映射,包含一个指向页描述符的指针和分配给该页框的线性地址。
struct page_address_map {
struct page *page;
void *virtual;
struct list_head list;
};
struct page_address_map {
struct page *page;
void *virtual;
struct list_head list;
};
struct page_address_map {
struct page *page;
void *virtual;
struct list_head list;
};
指向永久内核映射的专用页表。该页表包含在普通页表中,需要在初始化过程中计算其具体地址,再由pkmap_page_table指向,由此可以快速引用。
void __init permanent_kmaps_init(pgd_t *pgd_base) //持久内核映射,参数是cr3寄存器的值
{
pgd_t *pgd; //全局页目录
pud_t *pud; //上层页目录
pmd_t *pmd; //中间页目录
pte_t *pte; //页目录
unsigned long vaddr;
vaddr = PKMAP_BASE; //持久映射地址基址(虚拟地址)
page_table_range_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);//设置PKMAP_BASE~PKMAP_BASE+4MB线性地址段的页表
pgd = swapper_pg_dir + pgd_index(vaddr); //页全局目录地址
pud = pud_offset(pgd, vaddr);
pmd = pmd_offset(pud, vaddr);
pte = pte_offset_kernel(pmd, vaddr);
pkmap_page_table = pte; //指向页表中pkmbase开始的地方,这个部分是所有内核页表共享的部分
}
指向永久内核映射的专用页表。该页表包含在普通页表中,需要在初始化过程中计算其具体地址,再由pkmap_page_table指向,由此可以快速引用。
void __init permanent_kmaps_init(pgd_t *pgd_base) //持久内核映射,参数是cr3寄存器的值
{
pgd_t *pgd; //全局页目录
pud_t *pud; //上层页目录
pmd_t *pmd; //中间页目录
pte_t *pte; //页目录
unsigned long vaddr;
vaddr = PKMAP_BASE; //持久映射地址基址(虚拟地址)
page_table_range_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);//设置PKMAP_BASE~PKMAP_BASE+4MB线性地址段的页表
pgd = swapper_pg_dir + pgd_index(vaddr); //页全局目录地址
pud = pud_offset(pgd, vaddr);
pmd = pmd_offset(pud, vaddr);
pte = pte_offset_kernel(pmd, vaddr);
pkmap_page_table = pte; //指向页表中pkmbase开始的地方,这个部分是所有内核页表共享的部分
}
void __init permanent_kmaps_init(pgd_t *pgd_base) //持久内核映射,参数是cr3寄存器的值
{
pgd_t *pgd; //全局页目录
pud_t *pud; //上层页目录
pmd_t *pmd; //中间页目录
pte_t *pte; //页目录
unsigned long vaddr;
vaddr = PKMAP_BASE; //持久映射地址基址(虚拟地址)
page_table_range_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);//设置PKMAP_BASE~PKMAP_BASE+4MB线性地址段的页表
pgd = swapper_pg_dir + pgd_index(vaddr); //页全局目录地址
pud = pud_offset(pgd, vaddr);
pmd = pmd_offset(pud, vaddr);
pte = pte_offset_kernel(pmd, vaddr);
pkmap_page_table = pte; //指向页表中pkmbase开始的地方,这个部分是所有内核页表共享的部分
}
高端内存页框的数量。未开启PAE时为1024,开启PAE后为512。
高端内存页框的数量。未开启PAE时为1024,开启PAE后为512。
持久内核映射的起始地址。
计数器数组,包含LAST_PKMAP个项,描述对应页表项的使用情况。
计数器数组,包含LAST_PKMAP个项,描述对应页表项的使用情况。
值为0,对应页表项没有映射任何高端内存页框,可以使用;
值为0,对应页表项没有映射任何高端内存页框,可以使用;
值为1,对应页表项没有映射任何高端内存页框,但它不能使用,因为自其最后一次使用以来,其相应的TLB还未刷新;
值为1,对应页表项没有映射任何高端内存页框,但它不能使用,因为自其最后一次使用以来,其相应的TLB还未刷新;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2019-5-20 16:20
被极目楚天舒编辑
,原因: