Linux 内核中的 copy_from_user 如何在内部工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8265657/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does copy_from_user from the Linux kernel work internally?
提问by Santi1986
How exactly does the copy_from_user()
function work internally? Does it use any buffers or is there any memory mapping done, considering the fact that kernel does have the privilege to access the user memory space?
该copy_from_user()
功能在内部究竟是如何工作的?考虑到内核确实有权访问用户内存空间,它是否使用任何缓冲区或是否进行了任何内存映射?
采纳答案by caf
The implementation of copy_from_user()
is highly dependent on the architecture.
的实现copy_from_user()
高度依赖于架构。
On x86 and x86-64, it simply does a direct read from the userspace address and write to the kernelspace address, while temporarily disabling SMAP (Supervisor Mode Access Prevention) if it is configured. The tricky part of it is that the copy_from_user()
code is placed into a special region so that the page fault handler can recognise when a fault occurs within it. A memory protection fault that occurs in copy_from_user()
doesn't kill the process like it would if it is triggered by any other process-context code, or panic the kernel like it would if it occured in interrupt context - it simply resumes execution in a code path which returns -EFAULT
to the caller.
在 x86 和 x86-64 上,它只是从用户空间地址直接读取并写入内核空间地址,同时临时禁用 SMAP(超级管理模式访问保护)(如果已配置)。棘手的部分是将copy_from_user()
代码放置在一个特殊区域中,以便页面错误处理程序可以识别其中何时发生错误。发生在copy_from_user()
中的内存保护错误不会像由任何其他进程上下文代码触发那样杀死进程,或者像在中断上下文中发生时那样使内核恐慌 - 它只是在代码路径中恢复执行返回-EFAULT
给调用者。
回答by Praveen Felix
The implementation of copy_from_user()
system call is done using two buffers from different address spaces:
copy_from_user()
系统调用的实现是使用来自不同地址空间的两个缓冲区完成的:
- The user-space bufferin user virtual address space.
- The kernel-space bufferin kernel virtual address space.
- 在用户空间缓冲在用户的虚拟地址空间。
- 在内核空间的缓冲区在内核虚拟地址空间。
When the copy_from_user()
system call is invoked, data is copied from user buffer to kernel buffer.
当copy_from_user()
系统调用被调用时,数据从用户缓冲区复制到内核缓冲区。
A part (write operation) of character device driver code where copy_from_user()
is used is given below:
使用的字符设备驱动代码的一部分(写操作)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); }
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); }
回答by aas029
regarding "how bout copy_to_user since the kernel is passing on the kernel space address,how can a user space process access it"
关于“由于内核正在传递内核空间地址,因此 copy_to_user 怎么样,用户空间进程如何访问它”
A user space process can attempt to access any address. However, if the address is not mapped in that process user space (i.e. in the page tables of that process) or if there is a problem with the access like a write attempt to a read-only location, then a page fault is generated. Note that at least on the x86, every process has all the kernel space mapped in the lowest 1 gigabyte of that process's virtual address space, while the 3 upper gigabytes of the 4GB total address space (I'm using here the 32-bit classic case) are used for the process text (i.e. code) and data. A copy to or from user space is executed by the kernel code that is executing on behalf of the process and actually it's the memory mapping (i.e. page tables) of that process that are in-use during the copy. This takes place while execution is in kernel mode - i.e. privileged/supervisor mode in x86 language. Assuming the user-space code has passed a legitimate target location (i.e. an address properly mapped in that process address space) to have data copied to, copy_to_user, run from kernel context would be able to normally write to that address/region w/out problems and after the control returns to the user, user space also can read from this location setup by the process itself to start with. More interesting details can be found in chapters 9 and 10 of Understanding the Linux Kernel, 3rd Edition, By Daniel P. Bovet, Marco Cesati. In particular, access_ok() is a necessary but not sufficient validity check. The user can still pass addresses not belong to the process address space. In this case, a Page Fault exception will occur while the kernel code is executing the copy. The most interesting part is how the kernel page fault handler determines that the page fault in such case is not due to a bug in the kernel code but rather a bad address from the user (especially if the kernel code in question is from a kernel module loaded).
用户空间进程可以尝试访问任何地址。但是,如果该地址未映射到该进程的用户空间(即该进程的页表中),或者如果访问存在问题,例如对只读位置的写尝试,则会生成页面错误。请注意,至少在 x86 上,每个进程都将所有内核空间映射到该进程虚拟地址空间的最低 1 GB 中,而 4GB 总地址空间的 3 GB 上位(我在这里使用的是 32 位经典case)用于处理文本(即代码)和数据。到用户空间或从用户空间复制是由代表进程执行的内核代码执行的,实际上它是在复制过程中使用的该进程的内存映射(即页表)。这发生在执行处于内核模式时 - 即 x86 语言中的特权/主管模式。假设用户空间代码已经传递了一个合法的目标位置(即在该进程地址空间中正确映射的地址)以将数据复制到 copy_to_user,从内核上下文运行将能够正常写入该地址/区域,而无需输出问题和控制权返回给用户后,用户空间也可以从这个位置读取,由进程本身设置开始。更多有趣的细节可以在《Understanding the Linux Kernel, 3rd Edition》的第 9 章和第 10 章中找到,作者是 Daniel P. Bovet,Marco Cesati。特别是,access_ok() 是必要但不充分的有效性检查。用户仍然可以传递不属于进程地址空间的地址。在这种情况下,内核代码在执行副本时会发生 Page Fault 异常。
回答by Wang YanQing
The best answer has something wrong, copy_(from|to)_user
can't be used in interrupt context, they may sleep, copy_(from|to)_user
function can only be used in process context,
the process's page table include all the information that kernel need to access it, so kernel can direct access the user space address if we can make sure the page addressed is in memory, use copy_(from|to)_user
function, because they can check it for us and if the user space addressed page is not resident, it will fix it for us directly.
最佳答案有问题,copy_(from|to)_user
不能在中断上下文中使用,它们可能会休眠,copy_(from|to)_user
函数只能在进程上下文中使用,进程的页表包含内核需要访问它的所有信息,因此内核可以直接访问用户空间地址如果我们可以确定被寻址的页面在内存中,使用copy_(from|to)_user
函数,因为他们可以为我们检查它,如果用户空间寻址的页面不常驻,它会直接为我们修复它。