copy_from_user的疑惑
copy_from_user是从内核空间读取用户空间的数据。
copy_from_user(void *to, const void __user *from, unsigned long n)
32位操作系统,from是用户空间的指针,地址小于0XC0000000,to是内核空间指针,地址大于0XC0000000。
我知道操作用户空间地址,应该都是要经过GDT把虚拟地址转化为线性地址(在Linux中,经过GDT转化后,其实虚拟地址就是线性地址),然后通过CR3寄存器(CR3里面的地址应该是物理地址吧,由内核来负责装入)找到页表,页目录,然后把线性地址最终转化为物理地址。
操作内核空间的地址不太清楚,是按照上面步骤,通过GDT,CR3得到物理地址还是通过__pa这个宏,直接减去0XC000000这个偏移得到物理地址的呢?
from是用户空间的指针,是虚拟地址(其实就是线性地址),应该在用户的数据段里,要读from地址的内容,应该把该用户数据段所在的页表地址放到CR3寄存器里,然后转化为物理地址进行读取吧。
我看了copy_from_user的源代码,源码里面好像没有操作CR3寄存器的语句,没搞懂它到底是怎么读取用户空间的数据,然后写到内核空间中去的?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
地址轉換全部都是由MMU完成的
不管用户空间还是内核空间,逻辑地址到物理地址都是通过MMU(GDT,CR3)进行转化的。对于内核逻辑地址来说,通过MMU转化就相当于减去0xC0000000这个偏移地址,是这个意思吧?
copy_from_user是怎么读取用户空间的数据的呢?我看源代码最终就是调用mov语句实现的,它怎么知道用户空间地址是那个进程的?如果进行GDT,CR3这些转化的呢?
本帖最后由 zhanglong71 于 2011-06-16 21:58 编辑
1.开启分页功能后,能直接访问的只是线性地址。机器(x86)的寻址过程是:
逻辑地址---(分段单元)--->线性地址---(分页单元)--->物理地址
2."通过MMU转化就相当于减去0xC0000000这个偏移地址,是这个意思吧?"
这样说是不合适的。
内核线性地址减去0xC0000000得到物理地址,是因为在机器初始化页目录页表项数据时,将0-896MB的物理地址空间建立平板映射,也就是将0-896MB的物理地址空间映射到0xc0000000-0xc0000000+896MB的线性地址空间。并且这种关系只在此范围内才能成立。超出此范围的就不行了。
3.copy_from_user只能用在进程上下文中。
在调度器切换任务并加载进程的执行环境时,会将其进程的task_strcut结构中的相关数据项分别加载到GDTR和CR3.也就是每个进程都有自己的GDTR和CR3.
在调用copy_from_user时,其中的两个地址都可看成是线性地址。在访问此地址时,机器通过GDTR,CR3将其转换成物理地址后访问其中的数据,而这一转换过程是不可见的。
我来解释一下, 一个进程无论 内核还是用户空间都只有一个 页目录表, 内核调用copy_from_user的时候如果 内核映射页不存在就会发生异常,缺页异常中切换一下映射表,继续读取参数值。
根据大家的描述,是下面这个意思吧。
进程从用户态进入内核态不会引起CR3的变化。所有进程的内核空间都是一样的。
所有进程的页表中对应于线性地址范围为3g-4g的页目录是一致的,对应于内核空间,对应于相同的物理地址。对应于线性地址范围为0-3g之间的用户空间页目录不一致,对应不同的物理地址。
所有进程的页表中3g-4g的页目录都是相同的,这个是不是浪费了内存空间哈?