Linux内核源码分析—从用户空间复制数据到内核空间

本文主要参考《深入理解Linux内核》,结合2.6.11.1版的内核代码,分析从用户空间复制数据到内核空间函数。

1、不描述内核同步、错误处理、参数合法性验证相关的内容

2、源码摘自Linux内核2.6.11.1版

3、阅读本文请结合《深入理解Linux内核》第三版相关章节

4、本文会不定时更新

1、copy_from_user

函数功能:

从用户空间向内核空间复制数据

函数源码:

/**

* copy_from_user: - Copy a block of data fromuser space.

* @to:  Destination address, in kernel space.

* @from: Source address, in user space.

* @n:   Number of bytes to copy.

*

* Context: User context only.  This function may sleep.

*

* Copy data from user space to kernel space.

*

* Returns number of bytes that could not becopied.

* On success, this will be zero.

*

* If some data could not be copied, thisfunction will pad the copied

* data to the requested size using zero bytes.

*/

unsigned long

copy_from_user(void *to, const void__user *from, unsigned long n)

{

might_sleep();

BUG_ON((long)n < 0);

if(access_ok(VERIFY_READ, from, n))

n= __copy_from_user(to, from, n);

else

memset(to,0, n);

returnn;

}

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);

}

函数处理流程:

如果用户空间有读权限,调用__copy_from_user从用户空间复制数据到内核空间,__copy_from_user函数是__copy_from_user_inatomic函数的封装函数,__copy_from_user_inatomic的具体分析见本文;没有则把内核空间置0

2、__copy_from_user_inatomic

函数源码:

/**

* __copy_from_user: - Copy a block of datafrom user space, with less checking.

* @to:  Destination address, in kernel space.

* @from: Source address, in user space.

* @n:   Number of bytes to copy.

*

* Context: User context only.  This function may sleep.

*

* Copy data from user space to kernelspace.  Caller must check

* the specified block with access_ok() beforecalling this function.

*

* Returns number of bytes that could not becopied.

* On success, this will be zero.

*

* If some data could not be copied, thisfunction will pad the copied

* data to the requested size using zero bytes.

*/

static inline unsigned long

__copy_from_user_inatomic(void *to,const void __user *from, unsigned long n)

{

if(__builtin_constant_p(n)) {

unsignedlong ret;

switch(n) {

case1:

__get_user_size(*(u8*)to, from, 1, ret, 1);

returnret;

case2:

__get_user_size(*(u16*)to, from, 2, ret, 2);

returnret;

case4:

__get_user_size(*(u32*)to, from, 4, ret, 4);

returnret;

}

}

return__copy_from_user_ll(to, from, n);

}

函数处理流程:

1、__builtin_constant_p 是编译器gcc内置函数,用于判断一个值是否为编译时常量,如果是常数,函数返回1 ,否则返回0。

2、如果n是常量,这里先判断要拷贝的字节大小,如果是1,2,4字节的话,则调用函数__get_user_size来拷贝数据,具体分析参见本文

3、如果n不是常量,调用函数__copy_from_user_ll来拷贝数据,具体分析参见本文

4、__get_user_size

函数源码:

#define __get_user_size(x,ptr,size,retval,errret)           \

do {                               \

retval= 0;                        \

__chk_user_ptr(ptr);                   \

switch(size) {                        \

case1:__get_user_asm(x,ptr,retval,"b","b","=q",errret);break;    \

case2:__get_user_asm(x,ptr,retval,"w","w","=r",errret);break;    \

case4: __get_user_asm(x,ptr,retval,"l","","=r",errret);break; \

default:(x) = __get_user_bad();              \

}                           \

} while (0)

函数处理流程:

根据size是1、2、4字节,用不同的参数调用函数__get_user_asm来拷贝数据,具体分析参见本文

5、__get_user_asm

函数参数:

x=to

addr=from

err=4

itype=”b”/”w”/”l”

rtype=”b”/”w”/””

ltype=”=q”/”=r”/”=r”

errret=4

函数源码:

#define __get_user_asm(x, addr, err,itype, rtype, ltype, errret)  \

__asm____volatile__(                     \

/*movb/movw/movl addr, %b/w/“”1*/

"1: mov"itype"%2,%"rtype"1\n"         \

/**/

"2:\n"                       \

/*把下面的代码放入.fixup 节中*/

".section.fixup,\"ax\"\n"             \

/*err= errret */

"3: movl %3,%0\n"               \

/*把reg1即x置0*/

"   xor"itype"%"rtype"1,%"rtype"1\n"      \

/**/

"   jmp 2b\n"                \

/*恢复编译到前面保存的节中*/

".previous\n"                   \

/*这里指定异常表项,参见“参考文章3”*/

".section__ex_table,\"a\"\n"             \

/**/

"   .align 4\n"                 \

/*1b地址的指令引起异常,就跳转到3b*/

"   .long 1b,3b\n"                  \

/**/

".previous"                     \

:"=r"(err), ltype (x)                 \

:"m"(__m(addr)), "i"(errret), "0"(err))

函数处理流程:

输出部:

代码:: "=r"(err), ltype (x)

解释:

%0: err=r0

%1: x=寄存器 (ltype(=q/=r/=r))

输入部:

代码::"m"(__m(addr)), "i"(errret), "0"(err))

解释:

%2: addr内存单元

%3: errret直接操作数

%4: r0=err

损坏部:: 无

指令部:

1、具体指令含义参见代码注释

2、处理流程:执行1处的指令,成功则结束;失败,根据异常表项《.section __ex_table,\"a\"\n"》,跳转到3b处执行,设置寄存器值后跳转到2b处结束

注:关于异常表的介绍参见:

Linux异常表

http://www.cnblogs.com/chengxuyuancc/p/3428944.html

6、__copy_from_user_ll

函数源码:

unsigned long

__copy_from_user_ll(void *to, constvoid __user *from, unsigned long n)

{

BUG_ON((long)n< 0);

if(movsl_is_ok(to, from, n))

__copy_user_zeroing(to,from, n);

else

n= __copy_user_zeroing_intel(to, from, n);

returnn;

}

#define movsl_is_ok(a1,a2,n) \

__movsl_is_ok((unsignedlong)(a1),(unsigned long)(a2),(n))

static inline int__movsl_is_ok(unsigned long a1, unsigned long a2, unsigned long n)

{

#ifdef CONFIG_X86_INTEL_USERCOPY

if(n >= 64 && ((a1 ^ a2) & movsl_mask.mask))

return0;

#endif

return1;

}

函数处理流程:

这里没理解清楚CONFIG_X86_INTEL_USERCOPY和movsl_mask.mask的含义,待理解后补上

Linux内核源码分析—从用户空间复制数据到内核空间相关推荐

  1. v09.04 鸿蒙内核源码分析(调度故事) | 用故事说内核调度 | 百篇博客分析HarmonyOS源码

    子曰:"吾与回言终日,不违如愚.退而省其私,亦足以发.回也,不愚."<论语>:为政篇 百篇博客系列篇.本篇为: v09.xx 鸿蒙内核源码分析(调度故事篇) | 用故事 ...

  2. v70.05 鸿蒙内核源码分析(管道文件) | 如何降低数据流动成本 | 百篇博客分析OpenHarmony源码

    子曰:"其身正,不令而行:其身不正,虽令不从." <论语>:子路篇 百篇博客系列篇.本篇为: v70.xx 鸿蒙内核源码分析(管道文件篇) | 如何降低数据流动成本 文 ...

  3. Linux内核源码分析《进程管理》

    Linux内核源码分析<进程管理> 前言 1. Linux 内核源码分析架构 2. 进程原理分析 2.1 进程基础知识 2.2 Linux进程四要素 2.3 进程描述符 task_stru ...

  4. Linux内核源码分析之内存管理

    本文站的角度更底层,基本都是从Linux内核出发,会更深入.所以当你都读完,然后再次审视这些功能的实现和设计时,我相信你会有种豁然开朗的感觉. 1.页 内核把物理页作为内存管理的基本单元. 尽管处理器 ...

  5. Linux kernel 3.10内核源码分析--进程上下文切换

    一.疑问 进程调度时,当被选中的next进程不是current进程时,需要进行上下文切换. 进行上下文切换时,有一些问题不太容易理解,比如: 1.进程上下文切换必然发生在内核态吗? 2.上下文切换后原 ...

  6. Linux内核源码分析方法

    说明:这是一个刚接触内核三个月的同学,通过对一个内核特性的分析,总结出一套分析内核的方法. 一.内核源码之我见 Linux内核代码的庞大令不少人"望而生畏",也正因为如此,使得人们 ...

  7. Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】...

    原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...

  8. Linux内核源码分析方法—程序员进阶必备

    一.内核源码之我见 Linux内核代码的庞大令不少人"望而生畏",也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是 ...

  9. iostat IO统计原理linux内核源码分析----基于单通道SATA盘

    iostat IO统计原理linux内核源码分析----基于单通道SATA盘 先上一个IO发送submit_bio流程图,本文基本就是围绕该流程讲解. 内核版本 3.10.96 详细的源码注释:htt ...

最新文章

  1. html css网站开发兵书,程序员藏经阁--HTML+CSS网站开发兵书(附光盘)
  2. C语言学习笔记--预编译/宏定义/数组/参数传递/函数指针
  3. python3 多维数组 NumPy ndarray 简介
  4. 操作系统【逻辑空间、物理空间与页表大小的关系】【分页地址映射(方法+例题)】
  5. 400错误是什么原因_499错误是什么?499错误的原因及解决方法
  6. java并发编程之线程的基本介绍
  7. MYSQL索引创建与删除
  8. Hive 内嵌模式安装指导
  9. 《前端之路》之 JavaScript 高级技巧、高阶函数(一)
  10. 多视几何_计算两幅图像之间的基础矩阵F和一副图像上的点在另一福图像上的极线L
  11. 贪心算法详解(C++)
  12. Next主题美化博客
  13. 非参数统计:方法与应用(全书例题R语言实现)
  14. linux 文件名 序列号,在Linux中应如何查看系统硬件制造商、型号和序列号
  15. ffmpeg(七)合并音视频文件
  16. smartbi 安装教程
  17. zic - 时区编辑器
  18. 我的2016:做精彩的自己
  19. Office自定义功能区各功能图标间隔太大,与之前排版不一样,如何减少间隔距离?
  20. android画图之贝塞尔曲线讲解

热门文章

  1. APS54085 外围电路简单_调光无频闪 智能家居照明
  2. matlab里vSAS,科学网—【MATLAB】saveas和print保存图片的格式 - 叶瑞杰的博文
  3. mpvue 搭配 minui
  4. 磨金石教育摄影技能干货分享|那些酷炫的照片是怎么拍出来的?
  5. markdown 表情包大法
  6. Linux使用详解(进阶篇)
  7. android 动态、静态壁纸实现
  8. 怎么判断噎到没噎到_宝宝噎着的表现是什么 宝宝噎着症状
  9. tipask 3.5 出错get_class() expects parameter 1 to be object 解决方案及说明
  10. 《孙子兵法》中的企业领导艺术和方法