23

该功能在内部究竟是如何工作的copy_from_user()?考虑到内核确实有权访问用户内存空间这一事实,它是否使用任何缓冲区或是否完成了任何内存映射?

4

4 回答 4

31

的实现copy_from_user()高度依赖于架构。

在 x86 和 x86-64 上,它只是直接从用户空间地址读取并写入内核空间地址,同时如果已配置,则暂时禁用 SMAP(监督模式访问保护)。其中棘手的部分是copy_from_user()代码被放置在一个特殊的区域中,以便页面错误处理程序可以识别其中何时发生错误。发生的内存保护错误copy_from_user()不会像由任何其他进程上下文代码触发的那样杀死进程,也不会像在中断上下文中发生的那样使内核恐慌——它只是在代码路径中恢复执行返回-EFAULT给调用者。

于 2011-11-25T10:59:22.273 回答
9

关于“由于内核正在传递内核空间地址,用户空间进程如何访问它”

用户空间进程可以尝试访问任何地址。但是,如果地址没有映射到该进程的用户空间(即该进程的页表中),或者如果访问出现问题,例如对只读位置的写入尝试,则会产生页面错误。请注意,至少在 x86 上,每个进程都将所有内核空间映射到该进程虚拟地址空间的最低 1 GB,而 4GB 总地址空间的高 3 GB(我在这里使用 32 位经典case) 用于过程文本(即代码)和数据。到用户空间或从用户空间复制是由代表进程执行的内核代码执行的,实际上它是在复制期间正在使用的该进程的内存映射(即页表)。这发生在内核模式下执行时 - 即 x86 语言中的特权/主管模式。假设用户空间代码已通过合法目标位置(即在该进程地址空间中正确映射的地址)将数据复制到 copy_to_user,从内核上下文运行将能够正常写入该地址/区域而无需问题和控制权返回给用户后,用户空间也可以从这个位置读取进程本身设置的开始。更多有趣的细节可以在 Daniel P. Bovet 和 Marco Cesati 所著的《Understanding the Linux Kernel, 3rd Edition》的第​​ 9 章和第 10 章中找到。特别是,access_ok() 是必要但不充分的有效性检查。用户仍然可以传递不属于进程地址空间的地址。在这种情况下,内核代码在执行复制时会发生页面错误异常。

于 2012-01-21T22:08:43.770 回答
3

最佳答案有问题,copy_(from|to)_user不能在中断上下文中使用,它们可能会休眠,copy_(from|to)_user函数只能在进程上下文中使用,进程的页表包含内核需要访问它的所有信息,因此内核可以直接访问用户空间地址,如果我们可以确保寻址的页面在内存中,使用copy_(from|to)_user函数,因为他们可以为我们检查它,如果用户空间寻址的页面不是常驻的,它会直接为我们修复它。

于 2012-04-08T14:35:47.630 回答
0

系统调用的实现copy_from_user()是使用来自不同地址空间的两个缓冲区完成的:

  • 用户虚拟地址空间中的用户空间缓冲区
  • 内核虚拟地址空间中的内核空间缓冲区

copy_from_user()调用系统调用时,数据从用户缓冲区复制到内核缓冲区。

使用的字符设备驱动程序代码的一部分(写操作)copy_from_user()如下:

ssize_t cdev_fops_write(struct file *flip, const char __user *ubuf,
                        size_t count, loff_t *f_pos) 
{     
    unsigned int *kbuf;
    copy_from_user(kbuf, ubuf, count);
    printk(KERN_INFO "Data: %d",*kbuf); 
}
于 2011-11-25T13:00:26.447 回答