Linux中内核页表和进程页表有什么关系

发布时间:2022-01-05 09:41:49 作者:小新
来源:亿速云 阅读:326

这篇文章给大家分享的是有关Linux中内核页表和进程页表有什么关系的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

初学内核时,经常被“内核页表”和“进程页表”搞晕,不知道这到底是个啥东东,跟我们平时理解的页表有什么关系。

内核页表:即书上说的主内核页表,在内核中其实就是一段内存,存放在主内核页全局目录init_mm.pgd(swapper_pg_dir)中,硬件并不直接使用。

进程页表:每个进程自己的页表,放在进程自身的页目录task_struct.pgd中。

在保护模式下,从硬件角度看,其运行的基本对象为“进程”(或线程),而寻址则依赖于“进程页表”,在进程调度而进行上下文切换时,会进行页表的切换:即将新进程的pgd(页目录)加载到CR3寄存器中。从这个角度看,其实是完全没有用到“内核页表”的,那么“内核页表”有什么用呢?跟“进程页表”有什么关系呢?

1、内核页表中的内容为所有进程共享,每个进程都有自己的“进程页表”,“进程页表”中映射的线性地址包括两部分:

用户态

内核态

其中,内核态地址对应的相关页表项,对于所有进程来说都是相同的(因为内核空间对所有进程来说都是共享的),而这部分页表内容其实就来源于“内核页表”,即每个进程的“进程页表”中内核态地址相关的页表项都是“内核页表”的一个拷贝。

2、“内核页表”由内核自己维护并更新,在vmalloc区发生page fault时,将“内核页表”同步到“进程页表”中。以32位系统为例,内核页表主要包含两部分:

线性映射区

vmalloc区

其中,线性映射区即通过TASK_SIZE偏移进行映射的区域,对32系统来说就是0-896M这部分区域,映射对应的虚拟地址区域为TASK_SIZE-TASK_SIZE+896M。这部分区域在内核初始化时就已经完成映射,并创建好相应的页表,即这部分虚拟内存区域不会发生page fault。

vmalloc区,为896M-896M+128M,这部分区域用于映射高端内存,有三种映射方式:vmalloc、固定、临时,这里就不像述了。

以vmalloc为例(最常使用),这部分区域对应的线性地址在内核使用vmalloc分配内存时,其实就已经分配了相应的物理内存,并做了相应的映射,建立了相应的页表项,但相关页表项仅写入了“内核页表”,并没有实时更新到“进程页表中”,内核在这里使用了“延迟更新”的策略,将“进程页表”真正更新推迟到第一次访问相关线性地址,发生page fault时,此时在page fault的处理流程中进行“进程页表”的更新:

/*

     * 缺页地址位于内核空间。并不代表异常发生于内核空间,有可能是用户

     * 态访问了内核空间的地址。

     */

    if (unlikely(fault_in_kernel_space(address))) {

        if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) {

            //检查发生缺页的地址是否在vmalloc区,是则进行相应的处理

            if (vmalloc_fault(address) >= 0)

                return;

/*

  * 对于发生缺页异常的指针位于vmalloc区情况的处理,主要是将

  * 主内核页表向当前进程的内核页表同步。

  */

static noinline __kprobes int vmalloc_fault(unsigned long address)

{

    unsigned long pgd_paddr;

    pmd_t *pmd_k;

    pte_t *pte_k;

 

    /* Make sure we are in vmalloc area: */

    /* 区域检查 */

    if (!(address >= VMALLOC_START && address < VMALLOC_END))

        return -1;

 

    WARN_ON_ONCE(in_nmi());

 

    /*

     * Synchronize this task's top level page-table

     * with the 'reference' page table.

     *

     * Do _not_ use "current" here. We might be inside

     * an interrupt in the middle of a task switch..

     */

     /*获取pgd(最顶级页目录)地址,直接从CR3寄存器中读取。

     *不要通过current获取,因为缺页异常可能在上下文切换的过程中发生,

     *此时如果通过current获取,则可能会出问题*/

    pgd_paddr = read_cr3();

    //从主内核页表中,同步vmalloc区发生缺页异常地址对应的页表

    pmd_k = vmalloc_sync_one(__va(pgd_paddr), address);

    if (!pmd_k)

        return -1;

    //如果同步后,相应的PTE还不存在,则说明该地址有问题了

    pte_k = pte_offset_kernel(pmd_k, address);

    if (!pte_present(*pte_k))

        return -1;

 

    return 0;

}

感谢各位的阅读!关于“Linux中内核页表和进程页表有什么关系”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

推荐阅读:
  1. Bootstrap分页表格插件如何使用
  2. Python基于pandas如何爬取网页表格数据

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

linux

上一篇:android语音输入怎么实现

下一篇:Rainbond V5.1.7 更新带来哪些改进

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》