收藏 分享(赏)

基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析.doc

上传人:gnk289057 文档编号:4321215 上传时间:2018-12-23 格式:DOC 页数:11 大小:76.55KB
下载 相关 举报
基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析.doc_第1页
第1页 / 共11页
基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析.doc_第2页
第2页 / 共11页
基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析.doc_第3页
第3页 / 共11页
基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析.doc_第4页
第4页 / 共11页
基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析.doc_第5页
第5页 / 共11页
点击查看更多>>
资源描述

1、基于 ARM 构架(带 MMU)的 copy_from_user 与copy_to_user 详细分析在学习 Linux 内核驱动的时候,一开始就会碰到 copy_from_user 和copy_to_user 这两个常用的函数。这两个函数在内核使用的非常频繁,负责将数据从用户空间拷贝到内核空间以及将数据从内核空间拷贝到用户空间。在4年半前初学 Linux 内核驱动程序的时候,我只是知道这个怎么用,并没有很深入的分析这两个函数。这次研究内核模块挂载的时候,又碰到了它们。决定还是认真跟踪一下函数。首先这两个函数的原型在 arch/arm/include/asm/uaccess.h 文件中: st

2、atic 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; static inline unsigned long _must_check copy_to_user(void _user

3、 *to, const void *from, unsigned long n) if (access_ok(VERIFY_WRITE, to, n) n = _copy_to_user(to, from, n); return n; 这两个函数从结构上来分析,其实都可以分为两个部分:1、首先检查用户空间的地址指针是否有效(难点)2、调用_copy_from_user 和_copy_to_user 函数在这个分析中,我们先易后难。首先看看具体数据拷贝功能的_copy_from_user 和_copy_to_user 函数对于 ARM 构架,没有单独实现这两个函数,所以他们的代码位于includ

4、e/asm-generic/uaccess.h /* * 带有 MMU 的构架应该覆盖这两个函数 */ #ifndef _copy_from_user static inline _must_check long _copy_from_user(void *to, const void _user * from, unsigned long n) if (_builtin_constant_p(n) switch(n) case 1: *(u8 *)to = *(u8 _force *)from; return 0; case 2: *(u16 *)to = *(u16 _force *)fr

5、om; return 0; case 4: *(u32 *)to = *(u32 _force *)from; return 0; #ifdef CONFIG_64BIT case 8: *(u64 *)to = *(u64 _force *)from; return 0; #endif default: break; memcpy(to, (const void _force *)from, n); return 0; #endif #ifndef _copy_to_user static inline _must_check long _copy_to_user(void _user *t

6、o, const void *from, unsigned long n) if (_builtin_constant_p(n) switch(n) case 1: *(u8 _force *)to = *(u8 *)from; return 0; case 2: *(u16 _force *)to = *(u16 *)from; return 0; case 4: *(u32 _force *)to = *(u32 *)from; return 0; #ifdef CONFIG_64BIT case 8: *(u64 _force *)to = *(u64 *)from; return 0;

7、 #endif default: break; memcpy(void _force *)to, from, n); return 0; #endif点击(此处 )折叠或打开 GCC 的内建函数 _builtin_constant_p 用于判断一个值是否为编译时常数,如果参数值是常数,函数返回 1,否则返回 0。从这两个函数中可以看出其实结构是一样的,首先看看 n 是不是常数,如果是并为1、2 、4、8(64bit)则直接就用一个赋值语句拷贝数据。如果不是常数或 n 过大,则使用 memcpy 函数。而这个memcpy 函数位于 lib/string.c: #ifndef _HAVE_ARCH

8、_MEMCPY /* * memcpy - Copy one area of memory to another * dest: Where to copy to * src: Where to copy from * count: The size of the area. * * You should not use this function to access IO space, use memcpy_toio() * or memcpy_fromio() instead. */ void *memcpy(void *dest, const void *src, size_t coun

9、t) char *tmp = dest; const char *s = src; while (count-) *tmp+ = *s+; return dest; EXPORT_SYMBOL(memcpy); #endif这个函数其实就是一个简单的利用循环来数据拷贝,非常简单。好了如何拷贝数据我们已经了解了,现在我们来看看前面的用户空间指针检测函数 access_ok,这其实是一个宏定义,位于arch/arm/include/asm/uaccess.h 文件中: /* We use 33-bit arithmetic here. */ #define _range_ok(addr,size)

10、 ( unsigned long flag, roksum; _chk_user_ptr(addr); _asm_(“adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0“ : “= flag; ) #define access_ok(type,addr,size) (_range_ok(addr,size) = 0) 这个就比较麻烦了,涉及到了 C 语言中内联汇编,如果还不熟悉的朋友可以看看 ARM GCC 内嵌汇编手册 ,我也不是很熟。现在我们来仔细分析_range_ok 这个宏:(1)unsigned long flag, roksum;定义两

11、个变量 flag:保存结果的变量:非零代表地址无效,零代表地址可以访问。初始存放非零值(current_thread_info()-addr_limit),也就是当前进程的地址上限值。 roksum:保存要访问的地址范围末端,用于和当前进程地址空间限制数据做比较(2)_chk_user_ptr(addr);定义是一个空函数但是这个函数涉及到_CHECKER_ 宏的判断,_CHECKER_宏在通过 Sparse(Semantic Parser for C)工具对内核代码进行检查时会定义的。在使用 make C=1或 C=2时便会调用该工具,这个工具可以检查在代码中声明了 sparse 所能检查到

12、的相关属性的内核函数和变量。如果定义了_CHECKER_ ,在网上的资料中这样解释的:_chk_user_ptr 和_chk_io_ptr 在这里只声明函数,没有函数体,目的就是在编译过程中 Sparse 能够捕捉到编译错误,检查参数的类型。如果没有定义_CHECKER_ ,这就是一个空函数。(3)接下来的汇编,我适当地翻译如下:adds %1, %2, %3roksum = addr + size 这个操作影响状态位(目的是影响是进位标志C)以下的两个指令都带有条件 CC,也就是当 C=0的时候才执行。如果上面的加法指令进位了(C=1) ,则以下的指令都不执行,flag 就为初始值 curr

13、ent_thread_info()-addr_limit(非零值) ,并返回。如果没有进位(C=0) ,就执行下面的指令sbcccs %1, %1, %0 roksum = roksum - flag,也就是(addr + size)- (current_thread_info()-addr_limit) ,操作影响符号位。如果(addr + size)=(current_thread_info()-addr_limit) ,则 C=1如果(addr + size)addr_limit) ,则 C=0当 C=0的时候执行以下指令,否则跳过( flag 非零) 。movcc %0, #0flag

14、 = 0,给 flag 赋值 0(4)flag; 返回 flag 值综上所诉:_range_ok 宏其实等价于:如果(addr + size)=(current_thread_info()-addr_limit) ,返回非零值如果(addr + size)addr_limit ) ,返回零而 access_ok 就是检验将要操作的用户空间的地址范围是否在当前进程的用户地址空间限制中。这个宏的功能很简单,完全可以用 C实现,不是必须使用汇编。个人理解:由于这两个函数使用频繁,就使用汇编来实现部分功能来增加效率。从这里再次可以认识到,copy_from_user 与 copy_to_user 的使用是结合进程上下文的,因为他们要访问“user”的内存空间,这个“user”必须是某个特定的进程。通过上面的源码就知道,其中使用了 current_thread_info()来检查空间是否可以访问。如果在驱动中使用这两个函数,必须是在实现系统调用的函数中使用,不可在实现中断处理的函数中使用。如果在中断上下文中使用了,那代码就很可能操作了根本不相关的进程地址空间。其次由于操作的页面可能被换出,这两个函数可能会休眠,所以同样不可在中断上下文中使用。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 学术论文 > 毕业论文

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报