copy_to_user和copy_from_user
在linux内核中,我们将用户态数据拷贝到内核或则将用户态数据拷贝到内核,使用的是copy_from_user和copy_to_user。
然而在有些情况下,我们直接使用memcpy也不会出现错误,可以正常的将数据从内核态拷贝到用户态以及将数据从用户态拷贝到内核态,memcpy都不会发生错误。没发生错误是不是就不存在错误的情况呢?假如存在错误,这么哪些时侯使用memcpy会发生错误呢?memcpy和copy_{to/from}_user的区别又是哪些呢?
32位arm构架
在32位构架上,将用户态数据拷贝到内核时,首先分辨用户态数据的地址是否有效(也就是属于申请的虚拟地址范围):
1、当用户态虚拟地址有效时如何安装linux,这么在内核中使用memcpy和copy_{to/from}_user的过程是一样的,不会出现任何问题。虽然虚拟地址没有映射到化学显存,memcpy在内核态发生缺页后会由do_page_fault申请数学显存linux 用户空间使用sp,之后构建虚拟地址和化学地址的映射虚拟主机 linux,这个过程和copy_{to/from}_user一样。
2、当用户态虚拟地址无效时,内核态使用memcpy会造成缺页,之后调用do_page_fault申请数学显存。并且,因为虚拟地址是无效的,因而do_page_fault不能处理这些异常,也就不能构建虚拟地址和化学地址的映射关系,最终将造成kerneloops。
3、当用户态虚拟地址无效时,内核使用copy_{to/from}_user进行用户空间的数据拷贝,但是copy_{to/from}_user对所有显存操作的指令构建异常处理指令,也就是在对应的显存操作指令发生错误时,do_page_fault会跳转到异常处理处执行,处理后给用户空间返回错误提示,而不是直接报kerneloops。具体的过程如下:
64位arm构架
在64位arm构架下,linux可以开启内核不能访问用户空间地址的选项,此时linux 用户空间使用sp,我们内核就不能直接访问用户空间地址了,否则都会报错。既然不能访问用户空间地址,那在内核当中就不能使用memcpy来操作用户空间的数据,且只能使用copy_{to/from}_user。
这儿有个疑惑就是,copy_{to/from}_user操作的也是用户空间地址,为何她们不会有问题,而memcpy会有问题呢?这就要从arm64构架的特征说起了。
在arm64下,有两个页表:一个用户空间页表,一个内核空间页表。当我们开启了内核空间不能访问用户空间地址的选项时,进程在从用户空间切换到内核空间时,linux会将用户空间的页表设置为一个无效的页表。因而在kernel使用用户空间地址时,用户空间的页表是无效的,也就不能使用memcpy来交换用户空间和内核空间的数据了。而copy_{to/from}_user在交换用户空间和内核空间的数据时,会先将用户空间的页表设置回那种有效的页表,之后再执行数据的操作,所以copy_{to/from}_user可以放问用户空间地址,而其他函数(比如memcpy)不能。
同样,copy_{to/from}_user对用户空间地址操作的指令都在异常处理表中构建了一个映射,当操作的用户空间地址异常时,发生缺页异常后do_page_fault不能构建虚拟地址和化学地址的映射管理,do_page_fault会查找异常指令对应的异常处理函数,之后跳转到异常处理指令处执行,最后给用户态空间返回一个错误,而不是kerneloops。