Linux内核源码分析—从用户空间复制数据到内核空间
本文主要参考《深入理解Linux内核》,结合2.6.11.1版的内核代码,分析从用户空间复制数据到内核空间函数。
1、不描述内核同步、错误处理、参数合法性验证相关的内容
2、源码摘自Linux内核2.6.11.1版
3、阅读本文请结合《深入理解Linux内核》第三版相关章节
4、本文会不定时更新
1、copy_from_user
函数功能:
从用户空间向内核空间复制数据
函数源码:
/**
* copy_from_user: - Copy a block of data fromuser space.
* @to: Destination address, in kernel space.
* @from: Source address, in user space.
* @n: Number of bytes to copy.
*
* Context: User context only. This function may sleep.
*
* Copy data from user space to kernel space.
*
* Returns number of bytes that could not becopied.
* On success, this will be zero.
*
* If some data could not be copied, thisfunction will pad the copied
* data to the requested size using zero bytes.
*/
unsigned long
copy_from_user(void *to, const void__user *from, unsigned long n)
{
might_sleep();
BUG_ON((long)n < 0);
if(access_ok(VERIFY_READ, from, n))
n= __copy_from_user(to, from, n);
else
memset(to,0, n);
returnn;
}
static inline unsigned long
__copy_from_user(void *to, const void__user *from, unsigned long n)
{
might_sleep();
return __copy_from_user_inatomic(to, from, n);
}
函数处理流程:
如果用户空间有读权限,调用__copy_from_user从用户空间复制数据到内核空间,__copy_from_user函数是__copy_from_user_inatomic函数的封装函数,__copy_from_user_inatomic的具体分析见本文;没有则把内核空间置0
2、__copy_from_user_inatomic
函数源码:
/**
* __copy_from_user: - Copy a block of datafrom user space, with less checking.
* @to: Destination address, in kernel space.
* @from: Source address, in user space.
* @n: Number of bytes to copy.
*
* Context: User context only. This function may sleep.
*
* Copy data from user space to kernelspace. Caller must check
* the specified block with access_ok() beforecalling this function.
*
* Returns number of bytes that could not becopied.
* On success, this will be zero.
*
* If some data could not be copied, thisfunction will pad the copied
* data to the requested size using zero bytes.
*/
static inline unsigned long
__copy_from_user_inatomic(void *to,const void __user *from, unsigned long n)
{
if(__builtin_constant_p(n)) {
unsignedlong ret;
switch(n) {
case1:
__get_user_size(*(u8*)to, from, 1, ret, 1);
returnret;
case2:
__get_user_size(*(u16*)to, from, 2, ret, 2);
returnret;
case4:
__get_user_size(*(u32*)to, from, 4, ret, 4);
returnret;
}
}
return__copy_from_user_ll(to, from, n);
}
函数处理流程:
1、__builtin_constant_p 是编译器gcc内置函数,用于判断一个值是否为编译时常量,如果是常数,函数返回1 ,否则返回0。
2、如果n是常量,这里先判断要拷贝的字节大小,如果是1,2,4字节的话,则调用函数__get_user_size来拷贝数据,具体分析参见本文
3、如果n不是常量,调用函数__copy_from_user_ll来拷贝数据,具体分析参见本文
4、__get_user_size
函数源码:
#define __get_user_size(x,ptr,size,retval,errret) \
do { \
retval= 0; \
__chk_user_ptr(ptr); \
switch(size) { \
case1:__get_user_asm(x,ptr,retval,"b","b","=q",errret);break; \
case2:__get_user_asm(x,ptr,retval,"w","w","=r",errret);break; \
case4: __get_user_asm(x,ptr,retval,"l","","=r",errret);break; \
default:(x) = __get_user_bad(); \
} \
} while (0)
函数处理流程:
根据size是1、2、4字节,用不同的参数调用函数__get_user_asm来拷贝数据,具体分析参见本文
5、__get_user_asm
函数参数:
x=to
addr=from
err=4
itype=”b”/”w”/”l”
rtype=”b”/”w”/””
ltype=”=q”/”=r”/”=r”
errret=4
函数源码:
#define __get_user_asm(x, addr, err,itype, rtype, ltype, errret) \
__asm____volatile__( \
/*movb/movw/movl addr, %b/w/“”1*/
"1: mov"itype"%2,%"rtype"1\n" \
/**/
"2:\n" \
/*把下面的代码放入.fixup 节中*/
".section.fixup,\"ax\"\n" \
/*err= errret */
"3: movl %3,%0\n" \
/*把reg1即x置0*/
" xor"itype"%"rtype"1,%"rtype"1\n" \
/**/
" jmp 2b\n" \
/*恢复编译到前面保存的节中*/
".previous\n" \
/*这里指定异常表项,参见“参考文章3”*/
".section__ex_table,\"a\"\n" \
/**/
" .align 4\n" \
/*1b地址的指令引起异常,就跳转到3b*/
" .long 1b,3b\n" \
/**/
".previous" \
:"=r"(err), ltype (x) \
:"m"(__m(addr)), "i"(errret), "0"(err))
函数处理流程:
输出部:
代码:: "=r"(err), ltype (x)
解释:
%0: err=r0
%1: x=寄存器 (ltype(=q/=r/=r))
输入部:
代码::"m"(__m(addr)), "i"(errret), "0"(err))
解释:
%2: addr内存单元
%3: errret直接操作数
%4: r0=err
损坏部:: 无
指令部:
1、具体指令含义参见代码注释
2、处理流程:执行1处的指令,成功则结束;失败,根据异常表项《.section __ex_table,\"a\"\n"》,跳转到3b处执行,设置寄存器值后跳转到2b处结束
注:关于异常表的介绍参见:
http://www.cnblogs.com/chengxuyuancc/p/3428944.html
6、__copy_from_user_ll
函数源码:
unsigned long
__copy_from_user_ll(void *to, constvoid __user *from, unsigned long n)
{
BUG_ON((long)n< 0);
if(movsl_is_ok(to, from, n))
__copy_user_zeroing(to,from, n);
else
n= __copy_user_zeroing_intel(to, from, n);
returnn;
}
#define movsl_is_ok(a1,a2,n) \
__movsl_is_ok((unsignedlong)(a1),(unsigned long)(a2),(n))
static inline int__movsl_is_ok(unsigned long a1, unsigned long a2, unsigned long n)
{
#ifdef CONFIG_X86_INTEL_USERCOPY
if(n >= 64 && ((a1 ^ a2) & movsl_mask.mask))
return0;
#endif
return1;
}
函数处理流程:
这里没理解清楚CONFIG_X86_INTEL_USERCOPY和movsl_mask.mask的含义,待理解后补上