内核版本:2.6.14

CPU平台:arm本文引用地址:http://www.eepw.com.cn/article/201611/319994.htm

在内核空间和用户空间交换数据时,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):

[plain]view plaincopy

print?externint__get_user_1(void*);

externint__get_user_2(void*);

externint__get_user_4(void*);

externint__get_user_8(void*);

externint__get_user_bad(void);

#define__get_user_x(__r2,__p,__e,__s,__i...)\

__asm____volatile__(\

__asmeq("%0","r0")__asmeq("%1","r2")\//进行判断(#define__asmeq(x,y)".ifnc"x","y";.err;.endif\n\t")

"bl__get_user_"#__s\//根据参数调用不同的函数,此时r0=指向用户空间的指针,r2=内核空间的变量

:"=&r"(__e),"=r"(__r2)\

:"0"(__p)\

:__i,"cc")

#defineget_user(x,p)\

({\

constregistertypeof(*(p))__user*__pasm("r0")=(p);\//__p的数据类型和*(p)的指针数据类型是一样的,__p=p,且存放在r0寄存器中

registertypeof(*(p))__r2asm("r2");\//__r2的数据类型和*(p)的数据类型是一样的,且存放在r2寄存器中

registerint__easm("r0");\//定义__e,存放在寄存器r0,作为返回值

switch(sizeof(*(__p))){\//对__p所指向的对象长度进行检查,并根据长度调用响应的函数

case1:\

__get_user_x(__r2,__p,__e,1,"lr");\

break;\

case2:\

__get_user_x(__r2,__p,__e,2,"r3","lr");\

break;\

case4:\

__get_user_x(__r2,__p,__e,4,"lr");\

break;\

case8:\

__get_user_x(__r2,__p,__e,8,"lr");\

break;\

default:__e=__get_user_bad();break;\//默认处理

}\

x=__r2;\

__e;\

})

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

[plain]view plaincopy

print?.global__get_user_1

__get_user_1:

1:ldrbtr2,[r0]

movr0,#0

movpc,lr

.global__get_user_2

__get_user_2:

2:ldrbtr2,[r0],#1

3:ldrbtr3,[r0]

#ifndef__ARMEB__

orrr2,r2,r3,lsl#8

#else

orrr2,r3,r2,lsl#8

#endif

movr0,#0

movpc,lr

.global__get_user_4

__get_user_4:

4:ldrtr2,[r0]

movr0,#0

movpc,lr

.global__get_user_8

__get_user_8:

5:ldrtr2,[r0],#4

6:ldrtr3,[r0]

movr0,#0

movpc,lr

__get_user_bad_8:

movr3,#0

__get_user_bad:

movr2,#0

movr0,#-EFAULT

movpc,lr

.section__ex_table,"a"

.long1b,__get_user_bad

.long2b,__get_user_bad

.long3b,__get_user_bad

.long4b,__get_user_bad

.long5b,__get_user_bad_8

.long6b,__get_user_bad_8

.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 中定义了如下数据:

[plain]view plaincopy

print?.section__ex_table,"a"

.long1b,__get_user_bad//其中1b对应标号1处的指令,__get_user_bad是1处指令的修复指令。

.long2b,__get_user_bad

.long3b,__get_user_bad

.long4b,__get_user_bad

.long5b,__get_user_bad_8

.long6b,__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)。

[plain]view plaincopy

print?externint__put_user_1(void*,unsignedint);

externint__put_user_2(void*,unsignedint);

externint__put_user_4(void*,unsignedint);

externint__put_user_8(void*,unsignedlonglong);

externint__put_user_bad(void);

#define__put_user_x(__r2,__p,__e,__s)\

__asm____volatile__(\

__asmeq("%0","r0")__asmeq("%2","r2")\

"bl__put_user_"#__s\

:"=&r"(__e)\

:"0"(__p),"r"(__r2)\

:"ip","lr","cc")

#defineput_user(x,p)\

({\

constregistertypeof(*(p))__r2asm("r2")=(x);\

constregistertypeof(*(p))__user*__pasm("r0")=(p);\

registerint__easm("r0");\

switch(sizeof(*(__p))){\

case1:\

__put_user_x(__r2,__p,__e,1);\

break;\

case2:\

__put_user_x(__r2,__p,__e,2);\

break;\

case4:\

__put_user_x(__r2,__p,__e,4);\

break;\

case8:\

__put_user_x(__r2,__p,__e,8);\

break;\

default:__e=__put_user_bad();break;\

}\

__e;\

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

[plain]view plaincopy

print?.global__put_user_1

__put_user_1:

1:strbtr2,[r0]

movr0,#0

movpc,lr

.global__put_user_2

__put_user_2:

movip,r2,lsr#8

#ifndef__ARMEB__

2:strbtr2,[r0],#1

3:strbtip,[r0]

#else

2:strbtip,[r0],#1

3:strbtr2,[r0]

#endif

movr0,#0

movpc,lr

.global__put_user_4

__put_user_4:

4:strtr2,[r0]

movr0,#0

movpc,lr

.global__put_user_8

__put_user_8:

5:strtr2,[r0],#4

6:strtr3,[r0]

movr0,#0

movpc,lr

__put_user_bad:

movr0,#-EFAULT

movpc,lr

.section__ex_table,"a"

.long1b,__put_user_bad

.long2b,__put_user_bad

.long3b,__put_user_bad

.long4b,__put_user_bad

.long5b,__put_user_bad

.long6b,__put_user_bad

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

linux get_user,linux内核中的get_user和put_user相关推荐

  1. Linux 2.6内核中新的锁机制--RCU [转]

    2005 年 7 月 01 日 本文详细地介绍了 Linux 2.6 内核中新的锁机制 RCU(Read-Copy Update) 的实现机制,使用要求与典型应用. 一. 引言 众所周知,为了保护共享 ...

  2. linux英伟达显卡内核不匹配,硬核观察|Linus 破例在 Linux 5.11 内核中允许英伟达显卡驱动加塞...

    Linus 破例在 Linux 5.11 内核中允许英伟达显卡驱动加塞 据 cnbeta 报道,曾因闭源驱动对英伟达爆粗口的 Linus Torvalds,刚刚宣布了 Linux 5.11 内核版本的 ...

  3. Linux 2.6内核中新的锁机制--RCU

    转载自: Linux 2.6内核中新的锁机制--RCU 一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁(rwlock),它们使用起来非常简单,而且是一种 ...

  4. linux5.5 dev null,NULL TTY驱动程序将出现在Linux 5.2内核中

    虽然最初在内核邮件列表中首次提出这个驱动程序时,有人对它的实用性和实用性提出了一些问题,但是NULL TTY驱动程序将在即将到来的Linux 5.2内核周期中开始它的主线之旅. NULL TTY驱动程 ...

  5. linux put函数,Linux内核中的get_user和put_user

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

  6. Linux网络:内核中的网络参数(net.xxx.xxx)

    目录 nf_conntrack bridge-nf 反向路径过滤 TCP相关 ARP相关 ARP回收 ARP过滤 参考文档 nf_conntrack nf_conntrack是Linux内核连接跟踪的 ...

  7. Linux 4.13/4.14内核中带来的ULP(Upper Layer Protocol)

    序 过了一个很爽的国庆假期,跟小小的小男朋友家长一起回其老家尝到了潮汕美食,南澳岛捕鱼捕虾,海鲜撑到爆,回到深圳次日小小另一个小朋友家长又带我们到东莞长安尝到了正宗的恩施土家菜,几天下来喝了几顿爽酒, ...

  8. Linux 内核中的 Device Mapper 机制

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

  9. Linux 0.12内核的内存管理基础

    在Linux 0.12内核中,为了有效地使用机器中的物理内存,在系统初始化阶段内存被划分成几个功能区域. Linux内核程序占据在物理内存的开始部分,接下来是供硬盘或软盘等块设备使用的高速缓冲区部分, ...

最新文章

  1. 设计模式之监听器模式
  2. Win8Metro(C#)数字图像处理--2.12Sobel边缘检测
  3. P2017 [USACO09DEC]晕牛Dizzy Cows
  4. xcode 4.3 调试的时候显示汇编,怎么办
  5. eclipse注释日期格式修改
  6. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第36篇]Index Calculus算法
  7. 聚类算法的缺点_常用聚类算法
  8. 穿越障碍物JAVA编程_JAVA 基础编程练习题1 【程序 1 不死神兔】
  9. python发音翻译-Python translate()方法
  10. 【邮政编码识别】基于matlab灰度二值化邮政编码识别【含Matlab源码 788期】
  11. java源码-LinkedHashMap
  12. docucentre s2011默认登录密码
  13. 【软件测试】测试用例
  14. php银行卡号查询接口,银行卡归属地查询
  15. 激光雷达进入「规模化」上车周期?最大变数是什么?
  16. Python实现AI变脸
  17. 用js做购物界面的常用效果
  18. flask sqlalchemy 统计 查询
  19. HTML5 video/audio监听事件属性及方法
  20. 阿里云弹性公网IP(EIP)带宽最大值可选1000Mbps

热门文章

  1. Python统计列表中重复次数最多的前N个元素
  2. ESP32用python驱动GY-VL53L0X
  3. Exadata 机器介绍
  4. 在51单片机上使用DS1302
  5. OPC UA 质量代码 OPC quality codes 简介
  6. 华盛顿和华尔街在监管问题上喋喋不休(中)
  7. 新概念三英语学习Unit1
  8. 【直播回顾】Hello HarmonyOS应用篇第六课——短视频应用开发
  9. 低功耗蓝牙LE Audio Profile 详细介绍
  10. Python下安装谷歌插件