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


面的源码涉及到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. .global __get_user_2
  7. __get_user_2:
  8. 2:  ldrbt   r2, [r0], #1
  9. 3:  ldrbt   r3, [r0]
  10. #ifndef __ARMEB__
  11. orr r2, r2, r3, lsl #8
  12. #else
  13. orr r2, r3, r2, lsl #8
  14. #endif
  15. mov r0, #0
  16. mov pc, lr
  17. .global __get_user_4
  18. __get_user_4:
  19. 4:  ldrt    r2, [r0]
  20. mov r0, #0
  21. mov pc, lr
  22. .global __get_user_8
  23. __get_user_8:
  24. 5:  ldrt    r2, [r0], #4
  25. 6:  ldrt    r3, [r0]
  26. mov r0, #0
  27. mov pc, lr
  28. __get_user_bad_8:
  29. mov r3, #0
  30. __get_user_bad:
  31. mov r2, #0
  32. mov r0, #-EFAULT
  33. mov pc, lr
  34. .section __ex_table, "a"
  35. .long   1b, __get_user_bad
  36. .long   2b, __get_user_bad
  37. .long   3b, __get_user_bad
  38. .long   4b, __get_user_bad
  39. .long   5b, __get_user_bad_8
  40. .long   6b, __get_user_bad_8
  41. .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. #define __put_user_x(__r2,__p,__e,__s)                  \
  7. __asm__ __volatile__ (                   \
  8. __asmeq("%0", "r0") __asmeq("%2", "r2")         \
  9. "bl __put_user_" #__s               \
  10. : "=&r" (__e)                       \
  11. : "0" (__p), "r" (__r2)                 \
  12. : "ip", "lr", "cc")
  13. #define put_user(x,p)                           \
  14. ({                              \
  15. const register typeof(*(p)) __r2 asm("r2") = (x);   \
  16. const register typeof(*(p)) __user *__p asm("r0") = (p);\
  17. register int __e asm("r0");             \
  18. switch (sizeof(*(__p))) {               \
  19. case 1:                         \
  20. __put_user_x(__r2, __p, __e, 1);        \
  21. break;                      \
  22. case 2:                         \
  23. __put_user_x(__r2, __p, __e, 2);        \
  24. break;                      \
  25. case 4:                         \
  26. __put_user_x(__r2, __p, __e, 4);        \
  27. break;                      \
  28. case 8:                         \
  29. __put_user_x(__r2, __p, __e, 8);        \
  30. break;                      \
  31. default: __e = __put_user_bad(); break;         \
  32. }                           \
  33. __e;                            \
  34. })

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

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

linux内核中的get_user和put_user的更多相关文章

  1. (笔记)Linux内核中内存相关的操作函数

    linux内核中内存相关的操作函数 1.kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) ...

  2. Linux 内核中的 Device Mapper 机制

    本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

  3. 向linux内核中添加外部中断驱动模块

    本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

  4. Linux内核中双向链表的经典实现

    概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...

  5. Linux内核中的fastcall和asmlinkage宏

    代码中看见:#define _fastcall 所以了解下fastcall -------------------------------------------------------------- ...

  6. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  7. (十)Linux内核中的常用宏container_of

    Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...

  8. Apparmor——Linux内核中的强制访问控制系统

      AppArmor 因为最近在研究OJ(oline judge)后台的安全模块的实现,所以一直在研究Linux下沙箱的东西,同时发现了Apparmor可以提供访问控制. AppArmor(Appli ...

  9. KSM剖析——Linux 内核中的内存去耦合

    简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通 ...

随机推荐

  1. FreeBSD 路由详解

    在同一个局域网上的计算机是直接连通的,但是不同的网络上的计算机并没有直接相连,只能通过一台特殊的专用计算机 -路由器来完成连通.路由器连接有多个网络界面,每一个网络界面连接到一个相应的网络上,具有一个 ...

  2. hdu 4494 最小费用流

    思路:这题我在下午重现的时候就用的费用流做,可是各种悲催的超时,只是我一开始的那种建图方式多了一个二分查找. 戏剧性的是,求距离的返回值写成int型了,CodeBlock编译器又没有警告,然后就WA啊 ...

  3. 转:MediaCoder H.264格式编码参数设置及详解

    转: http://mediacoder.com.cn/node/81 由于现在大部分视频转码都选择H.264格式进行编码,同时CUDA编码的画质还达不到x264软编码的质量(如果你对画质无要求,可以 ...

  4. input美化 checkbox和radio样式

    input美化    checkbox和radio样式 看惯了input[checkbox]和input[radio]默认样式,有没有想要改变一下呢?比如下面的样式: 比起html默认的样式,上图这些 ...

  5. 将java项目打包成jar文件并在cmd运行

    保证java项目不报错能够通过运行 选择export 选择一个你要运行的程序入口Main方法 打开cmd,前提是你的javajdk及环境变量配置完成

  6. (火炬)MS SQL Server数据库案例教程

    (火炬)MS SQL Server数据库案例教程 创建数据库: CREATE DATABASE TDB //数据库名称 ON ( NAME=TDB_dat,//逻辑文件名 在创建数据库完成之后语句中引 ...

  7. Windows优化大师最新版 V7.99 Build 12.604发布

    本文由 www.169it.com 收集整理 Windows优化大师是一款功能强大的系统工具软件,它提供了全面有效且简便安全的系统检测.系统优化.系统清理.系统维护四大功能模块及数个附加的工具软件.使 ...

  8. 使用命令修改ip地址

    简述:以serverv 2012 r2为例 常用的几种,当然不全,希望能较快的速率记下一种便可 直接配置 1.      查看网卡的显示名称 2.      配置静态iP地址 3.      查看配置 ...

  9. WebService 的创建,部署和使用

    WebService,即Web服务,能使得运行在不同机器上的不同应用无须借助,专门的第三方软件或硬件,就可相互交换数据或集成. 第一次选择WebService,是为了替代数据库远程连接.我们都知道当S ...

  10. C# 上传图片前判断上传图片的宽和高

    网上找了好久没有找到... 最后百度到了这句话“由上传的HttpPostFile的InputStream创建一个Image对象,然后想怎么处置就随便你了”. HTML代码: <asp:FileU ...