zoukankan      html  css  js  c++  java
  • 都知道的copy_from_user

    Q:一直知道内核和用户态的数据交互前都需要 校验地址的合法性,一般都用copy_from/to_user完成数据拷贝,那么为什么要这样呢??

    A:看了一些blog以及Stack Overflow 主要说的是安全性:

    Linux Kernel: How does copy_to_user work?
    Why can't you just call, say, memcpy? Two reasons.
    One, the kernel is capable of writing to any memory. User process's can't. copy_to_user needs to check dst to ensure it is accessible and writable by the current process.
    Two, depending on the architecture, you can't simply copy data from kernel to user-space. You might need to do some special setup first, invalidate certain caches, or use special operations.
    而内核可以访问任意内存。copy_to_user需要 通过当前进程来 检查目标的可写权限   (而用户 进程无法检查内核空间目标地址的 写权限 )
    同时:检查指针所指的内存是否有效


     
    设想一下:我们在写file_opeteration的write函数时:write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)/read(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
    1、buf地址不合法
    2、buf地址合法,内核还没有给它映射物理地址
    3、如果buf是个一个内核地址, read的时候不就是内核到内核的拷贝么??
     
    所以为了解决上述问题就有了copy_xx_user-----------
    • 地址检查,不合法返回
    • copy_xx_user 使用精心布置的访存汇编实现,并指这个汇编指令所在的地址全部登记起来(称为extable表)。出现2时------>会发生缺页异常,进入内核do_page_fault流程;然后检查出错的PC地址是不是早已在extable登记好的,如果是,同表示该缺页异常是copy_from_user/copy_to_user函数产生的。最后才检查该地址是否为该进程的合法地址,如果是则分配物理页并处理,否则就是非法地址,把进程给杀死(发送sigsegv信号)


     
    顺便有几个疑问??参考:https://www.cnblogs.com/alantu2018/p/8527077.html   http://www.mysixue.com/?p=114#33   这些blog写的详细
    1、为什么会产生page fault?
    2、发生缺页的上下文是否可以位于内核态
    有可能在内核态,但这种情况下,发生缺页的地址只能位于用户态地址空间,而且也只能为exceptions table中预先定义好的异常,如果exceptions table中没有预先定义的处理,或者缺页的地址位于内核态地址空间,则表示错误,进入oops流程。通常属于异常,会导致oops。、、-----copy_xx_user干的就是这事
        /*
         * If we're in an interrupt, have no user context or are running
         * in an atomic region then we must not take the fault:
         */
        if (unlikely(in_atomic() || !mm)) {mm 为空表示内核线程
            bad_area_nosemaphore(regs, error_code, address);
            return;
        }

    3、发生缺页的地址是否可以位于内核态地址空间----
    有可能,发生缺页的内核态地址仅可能位于vmalloc区;在使用Vmalloc分配物理内存时,确实进行了映射,创建了页表项。
    但是,其修改的仅为内核的主内核页表,并没有更新相关进程的页表;进程访问时,相关进程的页表中并没有Vmalloc分配内存相应的页表项,所以会触发page fault
    //该分支表明发生缺页时是发生在访问内核空间时  
        if (unlikely(address >= TASK_SIZE)) {   
            //表示 代码是在内核态访问内核态不存在 的地址,转到vmalloc_fault处理分支,可能是访问了不连续的内核页面  
            if (!(error_code & 5))  
                goto vmalloc_fault;  
            //做相应出错处理  
            goto bad_area_nosemaphore;  
        }   

    关于内存管理文章见连接:

     https://files.cnblogs.com/files/codestack/fp%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86.zip

     
    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的处理流程中进行“进程页表”的更新:
  • 相关阅读:
    谈谈最近求职的那些事
    新的一年你该如何起飞
    JavaScript原型链和instanceof运算符的暧昧关系
    (译)详解javascript立即执行函数表达式(IIFE)
    一道有意思的笔试题引发的对于new操作符的思考
    javascript中prototype、constructor以及__proto__之间的三角关系
    这10道javascript笔试题你都会么
    汤姆大叔的6道javascript编程题题解
    打字机游戏Ⅱ之手速pk
    css3实践之摩天轮式图片轮播+3D正方体+3D标签云(perspective、transform-style、perspective-origin)
  • 原文地址:https://www.cnblogs.com/codestack/p/13019491.html
Copyright © 2011-2022 走看看