zoukankan      html  css  js  c++  java
  • linux内核中的get_user和put_user

    linux内核中的get_user和put_user

    在 内核空间和用户空间交换数据时,get_user和put_user是两个两用的函数。相对于copy_to_user和 copy_from_user(将在另一篇博客中分析),这两个函数主要用于完成一些简单类型变量(char、int、long等)的拷贝任务,对于一些 复合类型的变量,比如数据结构或者数组类型,get_user和put_user函数还是无法胜任,这两个函数内部将对指针指向的对象长度进行检查,在 arm平台上只支持长度为1,2,4,8的变量。下面我具体分析,首先看get_user的定义(linux/include/asm-arm /uaccess.h):

    1. extern int __get_user_1(void *);  
    2. extern int __get_user_2(void *);  
    3. extern int __get_user_4(void *);  
    4. extern int __get_user_8(void *);  
    5. extern int __get_user_bad(void);  
    6.   
    7. #define __get_user_x(__r2,__p,__e,__s,__i...)                 
    8.        __asm__ __volatile__ (                     
    9.         __asmeq("%0", "r0") __asmeq("%1", "r2")          // 进行判断(#define __asmeq(x, y)  ".ifnc " x "," y " ; .err ; .endif ")  
    10.         "bl __get_user_" #__s                //根据参数调用不同的函数,此时r0=指向用户空间的指针,r2=内核空间的变量  
    11.         : "=&r" (__e), "=r" (__r2)                
    12.         : "0" (__p)                       
    13.         : __i, "cc")  
    14.   
    15. #define get_user(x,p)                             
    16.     ({                                
    17.         const register typeof(*(p)) __user *__p asm("r0") = (p); //__p的数据类型和*(p)的指针数据类型是一样的,__p = p,且存放在r0寄存器中  
    18.         register typeof(*(p)) __r2 asm("r2");            //__r2的数据类型和*(p)的数据类型是一样的,且存放在r2寄存器中  
    19.         register int __e asm("r0");              //定义__e,存放在寄存器r0,作为返回值  
    20.         switch (sizeof(*(__p))) {                //对__p所指向的对象长度进行检查,并根据长度调用响应的函数  
    21.         case 1:                           
    22.             __get_user_x(__r2, __p, __e, 1, "lr");        
    23.                 break;                        
    24.         case 2:                           
    25.             __get_user_x(__r2, __p, __e, 2, "r3", "lr");      
    26.             break;                        
    27.         case 4:                           
    28.                 __get_user_x(__r2, __p, __e, 4, "lr");        
    29.             break;                        
    30.         case 8:                           
    31.             __get_user_x(__r2, __p, __e, 8, "lr");        
    32.                 break;                        
    33.         default: __e = __get_user_bad(); break;          //默认处理  
    34.         }                             
    35.         x = __r2;                         
    36.         __e;                              
    37.     })  

    上 面的源码涉及到gcc的内联汇编,不太了解的朋友可以参考前面的博客(http://blog.csdn.net/ce123/article /details/8209702)。继续,跟踪__get_user_1等函数的执行,它们的定义如下(linux/arch/arm/lib /getuser.S)。

    1. .global __get_user_1  
    2. __get_user_1:  
    3. 1:  ldrbt   r2, [r0]  
    4.     mov r0, #0  
    5.     mov pc, lr  
    6.   
    7.     .global __get_user_2  
    8. __get_user_2:  
    9. 2:  ldrbt   r2, [r0], #1  
    10. 3:  ldrbt   r3, [r0]  
    11. #ifndef __ARMEB__  
    12.     orr r2, r2, r3, lsl #8  
    13. #else  
    14.     orr r2, r3, r2, lsl #8  
    15. #endif  
    16.     mov r0, #0  
    17.     mov pc, lr  
    18.   
    19.     .global __get_user_4  
    20. __get_user_4:  
    21. 4:  ldrt    r2, [r0]  
    22.     mov r0, #0  
    23.     mov pc, lr  
    24.   
    25.     .global __get_user_8  
    26. __get_user_8:  
    27. 5:  ldrt    r2, [r0], #4  
    28. 6:  ldrt    r3, [r0]  
    29.     mov r0, #0  
    30.     mov pc, lr  
    31.   
    32. __get_user_bad_8:  
    33.     mov r3, #0  
    34. __get_user_bad:  
    35.     mov r2, #0  
    36.     mov r0, #-EFAULT  
    37.     mov pc, lr  
    38.   
    39. .section __ex_table, "a"  
    40.     .long   1b, __get_user_bad  
    41.     .long   2b, __get_user_bad  
    42.     .long   3b, __get_user_bad  
    43.     .long   4b, __get_user_bad  
    44.     .long   5b, __get_user_bad_8  
    45.     .long   6b, __get_user_bad_8  
    46. .previous  


    这 段代码都是单条汇编指令实现的内存操作,就不进行详细注解了。如果定义__ARMEB__宏,则是支持EABI的大端格式代码 (http://blog.csdn.net/ce123/article/details/8457491),关于大端模式和小端模式的详细介绍,可以 参考http://blog.csdn.net/ce123/article/details/6971544。这段代码在.section __ex_table, "a"之前都是常规的内存拷贝操纵,特殊的地方在于后面定义“__ex_table”section 。

    标 号1,2,...,6处是内存访问指令,如果mov的源地址位于一个尚未被提交物理页面的空间中,将产生缺页异常,内核会调用do_page_fault 函数处理这个异常,因为异常发生在内核空间,do_page_fault将调用search_exception_tables在“ __ex_table”中查找异常指令的修复指令,在上面这段带面的最后,“__ex_table”section 中定义了如下数据:

    1. .section __ex_table, "a"  
    2.     .long   1b, __get_user_bad //其中1b对应标号1处的指令,__get_user_bad是1处指令的修复指令。  
    3.     .long   2b, __get_user_bad  
    4.     .long   3b, __get_user_bad  
    5.     .long   4b, __get_user_bad  
    6.     .long   5b, __get_user_bad_8  
    7.     .long   6b, __get_user_bad_8  

    当标号1处发生缺页异常时,系统将调用do_page_fault提交物理页面,然后跳到__get_user_bad继续执行。get_user函数如果成果执行则返回1,否则返回-EFAULT。

    put_user用于将内核空间的一个简单类型变量x拷贝到p所指向的用户空间。该函数可以自动判断变量的类型,如果执行成功则返回0,否则返回-EFAULT。下面给出它们的定义(linux/include/asm-arm/uaccess.h)。

    1. extern int __put_user_1(void *, unsigned int);  
    2. extern int __put_user_2(void *, unsigned int);  
    3. extern int __put_user_4(void *, unsigned int);  
    4. extern int __put_user_8(void *, unsigned long long);  
    5. extern int __put_user_bad(void);  
    6.   
    7. #define __put_user_x(__r2,__p,__e,__s)                    
    8.        __asm__ __volatile__ (                     
    9.         __asmeq("%0", "r0") __asmeq("%2", "r2")           
    10.         "bl __put_user_" #__s                 
    11.         : "=&r" (__e)                         
    12.         : "0" (__p), "r" (__r2)                   
    13.         : "ip", "lr", "cc")  
    14.   
    15. #define put_user(x,p)                             
    16.     ({                                
    17.         const register typeof(*(p)) __r2 asm("r2") = (x);     
    18.         const register typeof(*(p)) __user *__p asm("r0") = (p);  
    19.         register int __e asm("r0");               
    20.         switch (sizeof(*(__p))) {                 
    21.         case 1:                           
    22.             __put_user_x(__r2, __p, __e, 1);          
    23.             break;                        
    24.         case 2:                           
    25.             __put_user_x(__r2, __p, __e, 2);          
    26.             break;                        
    27.         case 4:                           
    28.             __put_user_x(__r2, __p, __e, 4);          
    29.             break;                        
    30.         case 8:                           
    31.             __put_user_x(__r2, __p, __e, 8);          
    32.             break;                        
    33.         default: __e = __put_user_bad(); break;           
    34.         }                             
    35.         __e;                              
    36.     })  

    __put_user_1等函数的的定义如下(linux/arch/arm/lib/putuser.S)。

    1. .global __put_user_1  
    2. __put_user_1:  
    3. 1:  strbt   r2, [r0]  
    4.     mov r0, #0  
    5.     mov pc, lr  
    6.   
    7.     .global __put_user_2  
    8. __put_user_2:  
    9.     mov ip, r2, lsr #8  
    10. #ifndef __ARMEB__  
    11. 2:  strbt   r2, [r0], #1  
    12. 3:  strbt   ip, [r0]  
    13. #else  
    14. 2:  strbt   ip, [r0], #1  
    15. 3:  strbt   r2, [r0]  
    16. #endif  
    17.     mov r0, #0  
    18.     mov pc, lr  
    19.   
    20.     .global __put_user_4  
    21. __put_user_4:  
    22. 4:  strt    r2, [r0]  
    23.     mov r0, #0  
    24.     mov pc, lr  
    25.   
    26.     .global __put_user_8  
    27. __put_user_8:  
    28. 5:  strt    r2, [r0], #4  
    29. 6:  strt    r3, [r0]  
    30.     mov r0, #0  
    31.     mov pc, lr  
    32.   
    33. __put_user_bad:  
    34.     mov r0, #-EFAULT  
    35.     mov pc, lr  
    36.   
    37. .section __ex_table, "a"  
    38.     .long   1b, __put_user_bad  
    39.     .long   2b, __put_user_bad  
    40.     .long   3b, __put_user_bad  
    41.     .long   4b, __put_user_bad  
    42.     .long   5b, __put_user_bad  
    43.     .long   6b, __put_user_bad  
    44. .previous  

    put_user函数就不具体分析了。get_user和put_user仅能完成一些简单类型变量的拷贝任务,后面我们将分析copy_to_user和copy_from_user。

  • 相关阅读:
    解决deepin没有ll等命令的办法
    解决客户端Redis中文乱码问题
    Redis 常用命令操作
    Redis常用数据类型
    Redis 入门
    Ubuntu18.04 安装netstat
    Ubuntu18.04 安装redis
    常用sql:按照表中的某一列对数据进行分组,统计数据条数
    date( ) 日期函数
    tp5 apache 转 nginx 需要配置的伪静态
  • 原文地址:https://www.cnblogs.com/oracleloyal/p/5386344.html
Copyright © 2011-2022 走看看