copy_from_user
和copy_to_user
底层都是copy_user_generic
。其主体部分是正常复制数据,但是访问用户态数据时,可能会出现page
fault,这时硬件会自动跳转到内核提供的page fault handler
do_page_fault
,将page准备好之后再跳回原来的指令继续执行。但是如果page
fault处理失败了,比如这不是一个valid
address,那么内核会搜索这条指令对应的fixup
point,如果找到了,就跳转到这个fixup point。
参考:https://www.kernel.org/doc/Documentation/x86/exception-tables.txt
copy_user_generic
根据CPU的特性有三种实现:
static __always_inline __must_check unsigned long |
如果有ERMS
(Enhanced REP MOVSB/STOSB
instructions),就用copy_user_enhanced_fast_string
;否则,如果有REP
指令,就用copy_user_generic_string
;否则用通用的copy_user_generic_unrolled
。
内嵌汇编详解:C语言内嵌汇编学习笔记。ASM_OUTPUT2
中,=
表示既读又写,D
表示di
,作为第一个函数参数,S
表示si
,作为第二个函数参数,d
表示dx
,作为第三个函数参数,a
表示ax
,作为返回值。"1" (to), "2" (from), "3" (len)
其实是Input Operands
,其中1
、2
、3
分别表示对应的输入操作数的物理位置必须与第1、2、3个输出操作数的物理位置一致。"memory", "rcx", "r8", "r9", "r10", "r11"
是Clobbers
,表示内存、rcx
、r8
、r9
、r10
、r11
的内容被修改了。
¶
copy_user_enhanced_fast_string
现代CPU通常有ERMS
,所以先看copy_user_enhanced_fast_string
:
/* |
STAC: Set AC。CLAC: Clear AC。参考:https://www.felixcloutier.com/x86/stac
copy_user_handle_tail
可以看作是尽量多复制一些字节,从而精准定位到出错的那个字节:
/* |
¶
copy_user_generic_string
ENTRY(copy_user_generic_string) |
与copy_user_enhanced_fast_string
差不多,主要是先进行了rep movsq
,即先以8字节为单位做拷贝,再rep movsb
,即逐字节拷贝。
¶
copy_user_generic_unrolled
/* |
就是一个做了循环展开的拷贝。