免费注册 查看新帖 |

Chinaunix

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

为什么copy_mm的时候子进程不设置指向内核空间的页表项 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-04-13 20:12 |只看该作者 |倒序浏览
copy_mm在子进程中设置指向父进程用户空间的页表项,却没有设置指向内核空间的页表项,也就是说,子进程的pgd中768项以后都没设置,那子进程不是没法执行内核代码了

static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
{
        struct mm_struct * mm, *oldmm;
        int retval;

        tsk->min_flt = tsk->maj_flt = 0;
        tsk->nvcsw = tsk->nivcsw = 0;

        tsk->mm = NULL;
        tsk->active_mm = NULL;

        /*
         * Are we cloning a kernel thread?
         *
         * We need to steal a active VM for that..
         */
        oldmm = current->mm;
        if (!oldmm)
                return 0;

        if (clone_flags & CLONE_VM) {
                atomic_inc(&oldmm->mm_users);
                mm = oldmm;
                /*
                 * There are cases where the PTL is held to ensure no
                 * new threads start up in user mode using an mm, which
                 * allows optimizing out ipis; the tlb_gather_mmu code
                 * is an example.
                 */
                spin_unlock_wait(&oldmm->page_table_lock);  
                goto good_mm;
        }

        retval = -ENOMEM;
        mm = allocate_mm();
        if (!mm)
                goto fail_nomem;

        /* Copy the current MM stuff.. */
        memcpy(mm, oldmm, sizeof(*mm));
        if (!mm_init(mm))  // 为子进程分配一个新的pgd
                goto fail_nomem;

        if (init_new_context(tsk,mm))
                goto fail_nocontext;

        retval = dup_mmap(mm, oldmm); // 在子进程的页表中填入父进程的用户空间页
        if (retval)
                goto free_pt;

        mm->hiwater_rss = mm->rss;
        mm->hiwater_vm = mm->total_vm;

good_mm:
        tsk->mm = mm;
        tsk->active_mm = mm;
        return 0;

free_pt:
        mmput(mm);
fail_nomem:
        return retval;

fail_nocontext:
        /*
         * If init_new_context() failed, we cannot use mmput() to free the mm
         * because it calls destroy_context()
         */
        mm_free_pgd(mm);
        free_mm(mm);
        return retval;
}

论坛徽章:
6
金牛座
日期:2013-10-08 10:19:10技术图书徽章
日期:2013-10-14 16:24:09CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2013-11-24 19:26:19未羊
日期:2014-01-23 15:50:002015年亚洲杯之阿联酋
日期:2015-05-09 14:36:15
2 [报告]
发表于 2012-04-15 09:26 |只看该作者
回复 1# littlenewer
copy_mm在子进程中设置指向父进程用户空间的页表项,却没有设置指向内核空间的页表项,也就是说,子进程的pgd中768项以后都没设置,那子进程不是没法执行内核代码了

所有进程共享内核空间的页表,在mm_init中,会复制内核空间的页表swapper_pg_dir的768项以后的信息到新进程页目录的768项以后(1:3模型下)

   

论坛徽章:
0
3 [报告]
发表于 2012-04-16 11:07 |只看该作者
回复 2# 瀚海书香

我把代码都贴上来了,可是没找到你说的copy的过程

static struct mm_struct * mm_init(struct mm_struct * mm)
{
        atomic_set(&mm->mm_users, 1);
        atomic_set(&mm->mm_count, 1);
        init_rwsem(&mm->mmap_sem);
        INIT_LIST_HEAD(&mm->mmlist);
        mm->core_waiters = 0;
        mm->nr_ptes = 0;
        spin_lock_init(&mm->page_table_lock);
        rwlock_init(&mm->ioctx_list_lock);
        mm->ioctx_list = NULL;
        mm->default_kioctx = (struct kioctx)INIT_KIOCTX(mm->default_kioctx, *mm);
        mm->free_area_cache = TASK_UNMAPPED_BASE;

        if (likely(!mm_alloc_pgd(mm))) {
                mm->def_flags = 0;
                return mm;
        }
        free_mm(mm);
        return NULL;
}

static inline int mm_alloc_pgd(struct mm_struct * mm)
{
        mm->pgd = pgd_alloc(mm);
        if (unlikely(!mm->pgd))
                return -ENOMEM;
        return 0;
}

pgd_t *pgd_alloc(struct mm_struct *mm)
{
        int i;
        pgd_t *pgd = kmem_cache_alloc(pgd_cache, GFP_KERNEL);

        if (PTRS_PER_PMD == 1 || !pgd)
                return pgd;

        for (i = 0; i < USER_PTRS_PER_PGD; ++i) {
                pmd_t *pmd = kmem_cache_alloc(pmd_cache, GFP_KERNEL);
                if (!pmd)
                        goto out_oom;
                set_pgd(&pgd, __pgd(1 + __pa(pmd)));
        }
        return pgd;

out_oom:
        for (i--; i >= 0; i--)
                kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd)-1));
        kmem_cache_free(pgd_cache, pgd);
        return NULL;
}

   

论坛徽章:
16
2015亚冠之吉达阿赫利
日期:2015-08-17 11:21:462015年迎新春徽章
日期:2015-03-04 09:58:11酉鸡
日期:2014-12-07 09:06:19水瓶座
日期:2014-11-04 14:23:29天秤座
日期:2014-03-02 08:57:52双鱼座
日期:2014-02-22 13:07:56午马
日期:2014-02-14 11:08:18双鱼座
日期:2014-02-13 11:09:37卯兔
日期:2014-02-06 15:10:34子鼠
日期:2014-01-20 14:48:19戌狗
日期:2013-12-19 09:37:46射手座
日期:2013-12-19 09:33:47
4 [报告]
发表于 2012-04-16 11:59 |只看该作者
本帖最后由 embeddedlwp 于 2012-04-16 12:00 编辑

start_kernel->pgtable_cache_init:
  1. 652void __init pgtable_cache_init(void)
  2. 653{
  3. 654        if (PTRS_PER_PMD > 1) {
  4. 655                pmd_cache = kmem_cache_create("pmd",
  5. 656                                        PTRS_PER_PMD*sizeof(pmd_t),
  6. 657                                        PTRS_PER_PMD*sizeof(pmd_t),
  7. 658                                        0,
  8. 659                                        pmd_ctor,
  9. 660                                        NULL);
  10. 661                if (!pmd_cache)
  11. 662                        panic("pgtable_cache_init(): cannot create pmd cache");
  12. 663        }
  13. 664        pgd_cache = kmem_cache_create("pgd",
  14. 665                                PTRS_PER_PGD*sizeof(pgd_t),
  15. 666                                PTRS_PER_PGD*sizeof(pgd_t),
  16. 667                                0,
  17. 668                                pgd_ctor,
  18. 669                                PTRS_PER_PMD == 1 ? pgd_dtor : NULL);
  19. 670        if (!pgd_cache)
  20. 671                panic("pgtable_cache_init(): Cannot create pgd cache");
  21. 672}
复制代码
所以每次从pgd_cache分配一个对象的时候都会调用pgd_ctor函数,再看看pgd_ctor是个啥:
  1. 198void pgd_ctor(void *pgd, kmem_cache_t *cache, unsigned long unused)
  2. 199{
  3. 200        unsigned long flags;
  4. 201
  5. 202        if (PTRS_PER_PMD == 1)
  6. 203                spin_lock_irqsave(&pgd_lock, flags);
  7. 204
  8. 205        memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD,
  9. 206                        swapper_pg_dir + USER_PTRS_PER_PGD,
  10. 207                        (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
  11. 208
  12. 209        if (PTRS_PER_PMD > 1)
  13. 210                return;
  14. 211
  15. 212        pgd_list_add(pgd);
  16. 213        spin_unlock_irqrestore(&pgd_lock, flags);
  17. 214        memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t));
  18. 215}
  19. 216
复制代码

论坛徽章:
0
5 [报告]
发表于 2012-04-16 15:00 |只看该作者
回复 4# embeddedlwp

明白了,多谢
   

论坛徽章:
0
6 [报告]
发表于 2012-04-17 17:31 |只看该作者
再问一题,带CLONE_VM的copy_mm会不会导致父子共享用户栈?
从代码看似乎是这样,但是这样肯定出乱子

论坛徽章:
16
2015亚冠之吉达阿赫利
日期:2015-08-17 11:21:462015年迎新春徽章
日期:2015-03-04 09:58:11酉鸡
日期:2014-12-07 09:06:19水瓶座
日期:2014-11-04 14:23:29天秤座
日期:2014-03-02 08:57:52双鱼座
日期:2014-02-22 13:07:56午马
日期:2014-02-14 11:08:18双鱼座
日期:2014-02-13 11:09:37卯兔
日期:2014-02-06 15:10:34子鼠
日期:2014-01-20 14:48:19戌狗
日期:2013-12-19 09:37:46射手座
日期:2013-12-19 09:33:47
7 [报告]
发表于 2012-04-17 21:23 |只看该作者
回复 6# littlenewer


有CLONE_VM标志的应该是sys_vfork与sys_clone了。如果你怕子进程改掉父进程的用户栈,只能用sys_fork了。


   

论坛徽章:
0
8 [报告]
发表于 2012-04-18 09:23 |只看该作者
回复 7# embeddedlwp


你觉得两个进程共享一个用户栈没问题吗?我觉得肯定不对   

论坛徽章:
16
2015亚冠之吉达阿赫利
日期:2015-08-17 11:21:462015年迎新春徽章
日期:2015-03-04 09:58:11酉鸡
日期:2014-12-07 09:06:19水瓶座
日期:2014-11-04 14:23:29天秤座
日期:2014-03-02 08:57:52双鱼座
日期:2014-02-22 13:07:56午马
日期:2014-02-14 11:08:18双鱼座
日期:2014-02-13 11:09:37卯兔
日期:2014-02-06 15:10:34子鼠
日期:2014-01-20 14:48:19戌狗
日期:2013-12-19 09:37:46射手座
日期:2013-12-19 09:33:47
9 [报告]
发表于 2012-04-18 09:33 |只看该作者
本帖最后由 embeddedlwp 于 2012-04-18 09:34 编辑

回复 8# littlenewer


如果你用vfork就表示你允许child修改parent的堆栈,如果你不允许,可以使用fork啊。
既然你已经允许了,又有什么不对呢!


   

论坛徽章:
0
10 [报告]
发表于 2012-04-18 09:53 |只看该作者
回复 9# embeddedlwp

如果两个进程共享用户栈,这两个进程还能各自运行吗?我认为每个进程必须有自己的用户栈才可以运行
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP