学习驱动比较常用的两个函数,copy_to_user和copy_form_user。
因为传参都是传的地址,单因为内核空间不能直接访问用户空间的内存,所以内核空间要使用用户空间参数。必须进行地址转换。而这连个函数就是负责把对应用空间进程的内存和内核空间的内存地址转换。
首先我们先分析copy_to_user
copy_to_user函数的作用是把内核空间的参数拷贝拷贝到用户空间,通常用于read之类的函数使用。
从 修饰上上可以看到,这两个函数都要求里面对参数的有效性进行检查。
static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
n = __copy_to_user(to, from, n);
return n;
}
to 目标地址,这个地址是用户空间的地址;
from 源地址,这个地址是内核空间的地址;
n 将要拷贝的数据的字节数。
我们可以看到这里的检查使用的access_ok(VERIFY_WRITE, to, n)函数。
第一个参数VERIFY_WRITE表示内核向用户空间写数据。
而要检查的地址就是用户空间的地址。
在arm下因为是地址和内存统一编址的所以不检查,而是直接返回1。
但在其他平台很多距需要检查地址了。
第二个函数__copy_to_user则是主要负责拷贝数据的了。
这个函数完全硬件架构平台有关的函数了,而且不同版本的内核可能名称还不一样。
下面这个是2.6.35.7版本的内核。
unsigned long
__copy_to_user(void __user *to, const void *from, unsigned long n)
{
/*
* This test is stubbed out of the main function above to keep
* the overhead for small copies low by avoiding a large
* register dump on the stack just to reload them right away.
* With frame pointer disabled, tail call optimization kicks in
* as well making this test almost invisible.
*/
if (n < 64)
return __copy_to_user_std(to, from, n);
return __copy_to_user_memcpy(to, from, n);
}
太底层的我们就不分析了,贴出来,大家知道怎么找就可以,想深入分析自己对着汇编看就可以。
ENTRY(__copy_to_user_std)
WEAK(__copy_to_user)
#include "copy_template.S"
ENDPROC(__copy_to_user)
ENDPROC(__copy_to_user_std)
.pushsection .fixup,"ax"
.align 0
copy_abort_preamble
ldmfd sp!, {r1, r2, r3}
sub r0, r0, r1
rsb r0, r0, r2
copy_abort_end
.popsection
接下来我们分析copy_form_user
copy_from_user函数的作用是把用户空间的参数拷贝拷贝到内核空间,通常用于write之类的函数使用。
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
n = __copy_from_user(to, from, n);
else /* security hole - plug it */
memset(to, 0, n);
return n;
}
通过参数可以看到,
to 目标地址,这个地址是内核空间的地址;
from 源地址,这个地址是用户的地址;
n 将要拷贝的数据的字节数。
通过代码也可以看到,它也是要检查地址的。
检查通过的话就拷贝用户空间的数据到内核空间。
检查没通过的话,就直接把内核空间这段内存清空。
具体的__copy_from_user和__copy_to_user一样,都是通过硬件架构相关的的汇编代码拷贝数据的。
唯一要说明的是这个操作在内核层,通常是开启了MMU,所以要通过查页表,各种换算之后,才能知道具体的地址,之后再进行拷贝。