字符设备驱动驱动的读,写。如:
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
等函数中,filp是文件结构体指针,buf是用户空间内存的地址,该地址在内核空间不能直接读写,count是
要读写的字节数,f_pos是读的位置相对于文件开头的偏移。
copy_from_user()和copy_to_user()函数的作用:由于内核空间与用户空间的内存不能直接互访,因此借助
函数copy_to_user()完成内核空间到用户空间的复制,函数copy_from_user()完成用户空间到内核空间的复制。
其中access_ok(VERIFY_READ, from, n)检查当前需要访问的地址空间,有没有被其他空间访问,
如果当前进程被允许访问地址addr处的内存,函数返回真(1),否则为假(0)。
其中__copy_to_user(to, from, n);__copy_from_user(to, from, n);才函数实现部分。
函数源码定义在:linux/arch/alpha/lib/memcpy.c
原型:extern void *memcpy(void *dest, void *src, unsigned int count);
功能:由src所指内存区域复制count个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针
注:上述系列函数均返回不能复制的字节数,因此,完全复制成功,返回值0。
且__copy_to_user()和__copy_from_user()如果直接调用的时候,不会调用
access_ok(VERIFY_READ, from, n)检查函数,也就是不会检查当前需要访问的空间,
有没有被其他空间访问。
而get_user(),put_user()的作用:
复制的内存是简单类型,如char,int ,long等,则可以使用简单的put_user()和get_user()
get_user -- Get a simple variable from user space.
put_user -- Write a simple value into user space.
在put_user(x,ptr)定义如下:
x:用户内存空间的整型变量
ptr:用户空间的地址
下面只对put_user()函数分析:
和这个宏定义很类似。
该代码switch()执行的代码:
get_user()原理相同,请自己分析。
而__get_user(),__put_user()函数与上面一样,只是不会调用
access_ok(VERIFY_READ, from, n)检查函数,也就是不会检查当前需要访问的空间,
有没有被其他空间访问。
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
等函数中,filp是文件结构体指针,buf是用户空间内存的地址,该地址在内核空间不能直接读写,count是
要读写的字节数,f_pos是读的位置相对于文件开头的偏移。
copy_from_user()和copy_to_user()函数的作用:由于内核空间与用户空间的内存不能直接互访,因此借助
函数copy_to_user()完成内核空间到用户空间的复制,函数copy_from_user()完成用户空间到内核空间的复制。
源码:linux/arch/i386/lib/usercopy.c copy_to_user: - Copy a block of data into user space. @to: Destination address, in user space. @from: Source address, in kernel space. @n: Number of bytes to copy. unsigned long 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; } copy_from_user: - Copy a block of data from user space. @to: Destination address, in kernel space. @from: Source address, in user space. @n: Number of bytes to copy. unsigned long 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 memset(to, 0, n); return n; } |
其中access_ok(VERIFY_READ, from, n)检查当前需要访问的地址空间,有没有被其他空间访问,
如果当前进程被允许访问地址addr处的内存,函数返回真(1),否则为假(0)。
源码定义:linux/include/asm-m32r/uaccess.h
#define access_ok(type,addr,size) (likely(__range_ok(addr,size) == 0))
#else
static inline int access_ok(int type, const void *addr, unsigned long size)
{
extern unsigned long memory_start, memory_end;
unsigned long val = (unsigned long)addr;
return ((val >= memory_start) && ((val + size) < memory_end));
}
|
其中__copy_to_user(to, from, n);__copy_from_user(to, from, n);才函数实现部分。
源码:linux/include/asm-frv/uaccess.h static inline unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n) { might_sleep(); return __copy_to_user_inatomic(to, from, n); } #define __copy_to_user_inatomic(to, from, n) (memcpy(____force(to), (from), (n)), 0) 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); } #define __copy_from_user_inatomic(to, from, n) (memcpy((to), ____force(from), (n)), 0) |
函数源码定义在:linux/arch/alpha/lib/memcpy.c
原型:extern void *memcpy(void *dest, void *src, unsigned int count);
功能:由src所指内存区域复制count个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针
注:上述系列函数均返回不能复制的字节数,因此,完全复制成功,返回值0。
且__copy_to_user()和__copy_from_user()如果直接调用的时候,不会调用
access_ok(VERIFY_READ, from, n)检查函数,也就是不会检查当前需要访问的空间,
有没有被其他空间访问。
而get_user(),put_user()的作用:
复制的内存是简单类型,如char,int ,long等,则可以使用简单的put_user()和get_user()
get_user -- Get a simple variable from user space.
put_user -- Write a simple value into user space.
源码定义:linux/include/asm-i386/uaccess.h #define put_user(x,ptr) \ ({ int __ret_pu; \ __typeof__(*(ptr)) __pu_val; \ __chk_user_ptr(ptr); \ __pu_val = x; \ switch(sizeof(*(ptr))) { \ case 1: __put_user_1(__pu_val, ptr); break; \ case 2: __put_user_2(__pu_val, ptr); break; \ case 4: __put_user_4(__pu_val, ptr); break; \ case 8: __put_user_8(__pu_val, ptr); break; \ default:__put_user_X(__pu_val, ptr); break; \ } \ __ret_pu; \ }) |
在put_user(x,ptr)定义如下:
x:用户内存空间的整型变量
ptr:用户空间的地址
下面只对put_user()函数分析:
# define __chk_user_ptr(x) (void)0 取得ptr指向的0地址,然后 __pu_val = x;可以得到x的偏移地址,这样就可以指向x。 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) |
该代码switch()执行的代码:
#define __put_user_1(x, ptr) __asm__ __volatile__("call __put_user_1":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr)) #define __put_user_2(x, ptr) __asm__ __volatile__("call __put_user_2":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr)) #define __put_user_4(x, ptr) __asm__ __volatile__("call __put_user_4":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr)) #define __put_user_8(x, ptr) __asm__ __volatile__("call __put_user_8":"=a" (__ret_pu):"A" ((typeof(*(ptr)))(x)), "c" (ptr)) #define __put_user_X(x, ptr) __asm__ __volatile__("call __put_user_X":"=a" (__ret_pu):"c" (ptr)) |
get_user()原理相同,请自己分析。
而__get_user(),__put_user()函数与上面一样,只是不会调用
access_ok(VERIFY_READ, from, n)检查函数,也就是不会检查当前需要访问的空间,
有没有被其他空间访问。