1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Linux内核源码分析—从用户空间复制数据到内核空间

Linux内核源码分析—从用户空间复制数据到内核空间

时间:2020-05-18 03:01:34

相关推荐

Linux内核源码分析—从用户空间复制数据到内核空间

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处结束

注:关于异常表的介绍参见:

Linux异常表

/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的含义,待理解后补上

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。