免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 8768 | 回复: 15
打印 上一主题 下一主题

[内存管理] 问个ARM的页表问题,进程切换时内核页表如何处理的 [复制链接]

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:58:11
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-04-18 22:53 |只看该作者 |倒序浏览
本帖最后由 arm-linux-gcc 于 2013-04-18 23:04 编辑

内核中并没有使用armv7的TTBR1,只用了TTBR0,感觉这东西可以把表示内核映射的页表和表示用户空间映射的页表分离开,但是linux中并未用这东西
于是就没有使用分离页表,那么0-4G的页表在物理上必须要连续,那么就必须使得每个进程的页表都要同时包含用户空间和内核空间两部分了(物理上相邻)

当切换进程时,直接把CP15协处理器中的TTBR0指向新进程的页表物理基地址,就可以实现页表的切换,当然还有刷新TLB的操作(linux并没有用arm的FCSE,所以需要刷新TLB)

如果在旧进程中,如果使用了ioremap来映射一个设备,那么内核空间页表就变化了,也许是导致一个二级页中的某个entry变了
那么当切换进程时,新进程的页表物理基地址被填到了TTBR0中,那么此时新进程如何才能看到旧进程做的那个ioremap呢?
我要问的其实就是切换进程时,是如何保持新旧进程的内核页表部分的一致的,因为没有使用分离页表,所以每个进程都有一份内核页表的副本,但是如何保持这些副本的一致呢?




我自己的猜测是页表中表示内核部分的一级页表永远不会再变了,需要变化的部分都用二级页表来实现,进程的一级页表中3G-4G的部分一直都指向这些二级页表,于是就是各个进程的页表的内核部分(内容一样)会去指向这些二级页表,感觉这样开销是最小的
但是我没有找到这样的代码,对内存管理和进程管理实在不熟悉

不知道我这理解对不对
求大牛解答,谢谢


论坛徽章:
0
2 [报告]
发表于 2013-04-19 00:00 |只看该作者
猜测应该不对,对内核页面的修改操作都只会修改主内核页表swapper_pg_dir,

而其他process在访问内核页失败的时候,会先尝试与主内核页表同步。

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:58:11
3 [报告]
发表于 2013-04-19 00:07 |只看该作者
本帖最后由 arm-linux-gcc 于 2013-04-19 00:10 编辑

回复 2# onlyxuyang

与初始页表同步,这解释不通啊
fork的时候,子进程的页表是直接拷贝的父进程的吧

如果按照以下流程:
进程A做ioremap
进程A fork出一个B,B的页表应该是直接拷贝的A的吧
进程A iounmap,此时在进程A中,映射关系不存在了
进程B中的页表,之前那个ioremap的仍然存在(由于在B的页表中,映射关系仍然存在,他不会导致访问内核失败)


   

论坛徽章:
0
4 [报告]
发表于 2013-04-19 09:34 |只看该作者
http://bbs.chinaunix.net/thread-4074212-1-1.html


vmalloc, ioremap确是更新的内核主页表的。其他进程访问这些896m以上地址会有确页异常,进行处理的。ulk3讲的,x86确实有。但是arm的代码我没有找到怎么处理的。

论坛徽章:
0
5 [报告]
发表于 2013-04-20 19:24 |只看该作者
本帖最后由 junnyg 于 2013-04-20 19:25 编辑

回复 3# arm-linux-gcc
1. fork一个新进程时,其内核态页表项内容是从init进程的页表中拷贝过来的
do_fork->copy_process->copy_mm->dup_mm->mm_alloc_pgd->mm_alloc_pgd->pgd_alloc
arm
  1. #define pgd_alloc(mm)                        get_pgd_slow(mm)
复制代码
/*
* need to get a 16k page for level 1
*/
pgd_t *get_pgd_slow(struct mm_struct *mm)
{
        pgd_t *new_pgd, *init_pgd;
        pmd_t *new_pmd, *init_pmd;
        pte_t *new_pte, *init_pte;

        new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 2);
        if (!new_pgd)
                goto no_pgd;

        memset(new_pgd, 0, FIRST_KERNEL_PGD_NR * sizeof(pgd_t));

        [fly]/*
         * Copy over the kernel and IO PGD entries
         */
        init_pgd = pgd_offset_k(0);
        memcpy(new_pgd + FIRST_KERNEL_PGD_NR, init_pgd + FIRST_KERNEL_PGD_NR,
                       (PTRS_PER_PGD - FIRST_KERNEL_PGD_NR) * sizeof(pgd_t));
[/fly]

       ... ...
}
x86

  1. pgd_t *pgd_alloc(struct mm_struct *mm)
  2. {
  3.         ... ...
  4.         pgd_ctor(pgd);
  5.         ... ...
  6. }
复制代码
static void pgd_ctor(pgd_t *pgd)
{
        /* If the pgd points to a shared pagetable level (either the
           ptes in non-PAE, or shared PMD in PAE), then just copy the
           references from swapper_pg_dir. */
        if (PAGETABLE_LEVELS == 2 ||
            (PAGETABLE_LEVELS == 3 && SHARED_KERNEL_PMD) ||
            PAGETABLE_LEVELS == 4) {
                clone_pgd_range(pgd + KERNEL_PGD_BOUNDARY,
                                swapper_pg_dir + KERNEL_PGD_BOUNDARY,
                                KERNEL_PGD_PTRS);

                paravirt_alloc_pmd_clone(__pa(pgd) >> PAGE_SHIFT,
                                         __pa(swapper_pg_dir) >> PAGE_SHIFT,
                                         KERNEL_PGD_BOUNDARY,
                                         KERNEL_PGD_PTRS);
        }

        /* list required to sync kernel mapping updates */
        if (!SHARED_KERNEL_PMD)
                pgd_list_add(pgd);
}
回复 4# blake326
2. 内核态页表项何时更改 & 同步
内核页表项会更改并同步的只有vmalloc的映射区,包括ioremap、vmalloc建立的映射,这两个函数调用时会对本进程和init进程的内核空间页表进行修改;其他进程会在缺页异常中从init进程的页表中同步过来,x86在do_page_fault里的vmalloc_fault分支处理里修改,arm则在一级页表解析异常处理函数do_translate_fault中做处理
x86

  1. /*
  2. * This routine handles page faults.  It determines the address,
  3. * and the problem, and then passes it off to one of the appropriate
  4. * routines.
  5. */
  6. dotraplinkage void __kprobes
  7. do_page_fault(struct pt_regs *regs, unsigned long error_code)
  8. {
  9.         ... ...
  10.         if (unlikely(fault_in_kernel_space(address))) {
  11.                 if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) {
  12.                         if (vmalloc_fault(address) >= 0)
  13.                                 return;

  14.                         if (kmemcheck_fault(regs, address, error_code))
  15.                                 return;
  16.                 }

  17.                 ... ...
  18.         }
  19.          ... ...

  20. }
复制代码
vmalloc_fault->vmalloc_sync_one
32位:
vmalloc_fault->vmalloc_sync_one
         if (!pmd_present(*pmd))
                set_pmd(pmd, *pmd_k);

        else
                BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k));
64位:
vmalloc_fault
        if (pgd_none(*pgd))
                set_pgd(pgd, *pgd_ref);

        else
                BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref));
arm的处理在一级页目录解析异常处理函数do_translation_fault中处理
arm的数据异常处理表:

  1. static struct fsr_info ifsr_info[] = {
  2.         ... ...
  3.         { do_translation_fault,        SIGSEGV, SEGV_MAPERR,        "section translation fault"           },
  4.         { do_bad,                SIGSEGV, SEGV_ACCERR,        "page access flag fault"           },
  5.         { do_page_fault,        SIGSEGV, SEGV_MAPERR,        "page translation fault"           },
  6.         { do_bad,                SIGBUS,         0,                "external abort on non-linefetch"  },
  7.         { do_bad,                SIGSEGV, SEGV_ACCERR,        "section domain fault"                   },
  8.         { do_bad,                SIGBUS,  0,                "unknown 10"                           },
  9.         { do_bad,                SIGSEGV, SEGV_ACCERR,        "page domain fault"                   },
  10.         { do_bad,                SIGBUS,         0,                "external abort on translation"           },
  11.         { do_sect_fault,        SIGSEGV, SEGV_ACCERR,        "section permission fault"           },
  12.         { do_bad,                SIGBUS,         0,                "external abort on translation"           },
  13.         { do_page_fault,        SIGSEGV, SEGV_ACCERR,        "page permission fault"                   },
  14.          ... ...
  15. };
复制代码
缺页异常处理流程
entry-armv.S->__dabt_svc ->do_DataAbort ->do_translation_fault
       if (!pgd_present(*pgd))
                set_pgd(pgd, *pgd_k)

论坛徽章:
0
6 [报告]
发表于 2013-04-20 19:44 |只看该作者
回复 5# junnyg
感谢回复~ 我也只是知道这个概念而已~ 并不确定三楼的问题怎么回答~~

一般不查bug的话 我是不会看那么细的 哈哈 ^^


   

论坛徽章:
0
7
发表于 2013-04-20 19:46
回复 3# arm-linux-gcc

kernel区终于出现arm相关的帖子了~ 很开心啊~
这个才是正常的啊 x86上和内核相关的开发真的很少很少啊
反而是做arm嵌入式的看kernel查bug的可能性大一点

和arm架构相关的帖子很少这个真的很反常啊


   

论坛徽章:
0
8 [报告]
发表于 2013-04-20 19:48 |只看该作者
回复 6# onlyxuyang
借楼主的问题来指引我思考,我觉得这种学习方式还是很不错的~


   

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:58:11
9 [报告]
发表于 2013-04-20 23:03 |只看该作者
回复 5# junnyg

谢谢回复,太详细了,

还有个问题,我不知道我理解的对不对:
进程从内核空间退回到用户空间时,需要清空自己的vmalloc zone区域的一级页表项,
如果不清空,那么下次再进入内核时,就有可能与内核主页表中的内容不一致了(比如发生了进程切换,并且被另外一个进程做了iounmap)


论坛徽章:
0
10 [报告]
发表于 2013-04-21 09:42 |只看该作者
本帖最后由 junnyg 于 2013-04-21 10:58 编辑

回复 9# arm-linux-gcc
不会不一致的,在缺页异常中对页表项的修改是引用的方式,存页表的只有一块内存,一个进程做iounmap,其他进程都能感知到,但是逻辑是要自己控制的,不能A做了ioremap,B还去访问这块空间,会导致内核panic(vmalloc/vfree的场景也一样)

   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP