linux内核中的copy_to_user和copy_from_user(一)
来源:互联网 发布:python 字典嵌套 编辑:程序博客网 时间:2024/05/29 18:18
转自:
http://m.blog.csdn.net/blog/ce123/8454226
linux内核中的copy_to_user和copy_from_user(一)
Kernel version:2.6.14
CPU architecture:ARM920T
Author:ce123(http://blog.csdn.net/ce123)
1.copy_from_user
在学习Linux内核驱动的时候,经常会碰到copy_from_user和copy_to_user这两个函数,设备驱动程序中的ioctl函数就经常会用到。这两个函数负责在用户空间和内核空间传递数据。首先看看它们的定义(linux/include/asm-arm/uaccess.h),先看copy_from_user:
static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n){if (access_ok(VERIFY_READ, from, n))n = __arch_copy_from_user(to, from, n);else /* security hole - plug it */memzero(to, n);return n;}
先看函数的三个参数:*to是内核空间的指针,*from是用户空间指针,n表示从用户空间想内核空间拷贝数据的字节数。如果成功执行拷贝操作,则返回0,否则返回还没有完成拷贝的字节数。
这个函数从结构上来分析,其实都可以分为两个部分:- 首先检查用户空间的地址指针是否有效;
- 调用__arch_copy_from_user函数。
1.1.access_ok
access_ok用来对用户空间的地址指针from作某种有效性检验,这个宏和体系结构相关,在arm平台上为(linux/include/asm-arm/uaccess.h):
#define __range_ok(addr,size) ({ \unsigned long flag, sum; \__chk_user_ptr(addr);\__asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \: "=&r" (flag), "=&r" (sum) \: "r" (addr), "Ir" (size), "0" (current_thread_info()->addr_limit) \: "cc"); \flag; })#define access_ok(type,addr,size)(__range_ok(addr,size) == 0)可以看到access_ok中第一个参数type并没有用到,__range_ok的作用在于判断addr+size之后是否还在进程的用户空间范围之内。下面我们具体看一下。这段代码涉及到GCC内联汇编,不懂的朋友可以先看看这篇博客(http://blog.csdn.net/ce123/article/details/8209702)。
(1)unsigned long flag, sum;\\定义两个变量
- flag:保存结果的变量:非零代表地址无效,零代表地址可以访问。初始存放非零值(current_thread_info()->addr_limit),也就是当前进程的地址上限值。
- sum:保存要访问的地址范围末端,用于和当前进程地址空间限制数据做比较。
这个函数涉及到__CHECKER__宏的判断,__CHECKER__宏在通过Sparse(Semantic Parser for C)工具对内核代码进行检查时会定义的。在使用make C=1或C=2时便会调用该工具,这个工具可以检查在代码中声明了sparse所能检查到的相关属性的内核函数和变量。
- 如果定义了__CHECKER__,__chk_user_ptr和__chk_io_ptr在这里只声明函数,没有函数体,目的就是在编译过程中Sparse能够捕捉到编译错误,检查参数的类型。
- 如果没有定义__CHECKER__,这就是一个空函数。
请看具体的定义(linux/compiler.h):
#ifdef __CHECKER__...extern void __chk_user_ptr(void __user *);extern void __chk_io_ptr(void __iomem *);#else...# define __chk_user_ptr(x) (void)0# define __chk_io_ptr(x) (void)0...#endif(3)接下来是汇编:
adds %1, %2, %3
sum = addr + size 这个操作影响状态位(目的是影响是进位标志C),以下的两个指令都带有条件CC,也就是当C=0的时候才执行。
如果上面的加法指令进位了(C=1),则以下的指令都不执行,flag就为初始值current_thread_info()->addr_limit(非0),并返回。
如果没有进位(C=0),就执行下面的指令:
sbcccs %1, %1, %0
sum = sum - flag - 1,也就是(addr + size) - (current_thread_info()->addr_limit) - 1,操作影响符号位。
如果(addr + size) >= (current_thread_info()->addr_limit) - 1,则C=1
如果(addr + size) < (current_thread_info()->addr_limit) - 1,则C=0
当C=0的时候执行以下指令,否则跳过(flag非零)。
movcc %0, #0
flag = 0,给flag赋值0。
综上所述:__range_ok宏其实等价于:
- 如果(addr + size) >= (current_thread_info()->addr_limit) - 1,返回非零值
- 如果(addr + size) < (current_thread_info()->addr_limit),返回零
从这里再次可以认识到,copy_from_user的使用是结合进程上下文的,因为他们要访问“user”的内存空间,这个“user”必须是某个特定的进程。通过上面的源码就知道,其中使用了current_thread_info()来检查空间是否可以访问。如果在驱动中使用这两个函数,必须是在实现系统调用的函数中使用,不可在实现中断处理的函数中使用。如果在中断上下文中使用了,那代码就很可能操作了根本不相关的进程地址空间。其次由于操作的页面可能被换出,这两个函数可能会休眠,所以同样不可在中断上下文中使用。
1.2.__arch_copy_from_user
#define USER(x...)\9999:x;\.section __ex_table,"a";\.align3;\.long9999b,9001f;\.previous
.long9999b,9001f;其中9999b对应标号9999处的指令,9001f是9001处的指令,是9999b处指令的修复指令。这样,当标号9999处发生缺页异常时,系统将调用do_page_fault提交物理页面,然后跳到9001继续执行。
/* Prototype: unsigned long __arch_copy_from_user(void *to,const void *from,unsigned long n); * Purpose : copy a block from user memory to kernel memory * Params : to - kernel memory * : from - user memory * : n - number of bytes to copy * Returns : Number of bytes NOT copied. */.cfu_dest_not_aligned:rsbip, ip, #4cmpip, #2USER(ldrbtr3, [r1], #1)@ May faultstrbr3, [r0], #1USER(ldrgebtr3, [r1], #1)@ May faultstrgebr3, [r0], #1USER(ldrgtbtr3, [r1], #1)@ May faultstrgtbr3, [r0], #1subr2, r2, ipb.cfu_dest_alignedENTRY(__arch_copy_from_user)stmfdsp!, {r0, r2, r4 - r7, lr}cmpr2, #4blt.cfu_not_enoughPLD(pld[r1, #0])PLD(pld[r0, #0])andsip, r0, #3bne.cfu_dest_not_aligned.cfu_dest_aligned:andsip, r1, #3bne.cfu_src_not_aligned/* * Seeing as there has to be at least 8 bytes to copy, we can * copy one word, and force a user-mode page fault... */.cfu_0fupi:subsr2, r2, #4addmiip, r2, #4bmi.cfu_0nowordsUSER(ldrtr3, [r1], #4)strr3, [r0], #4movip, r1, lsl #32 - PAGE_SHIFT@ On each page, use a ld/st??t instructionrsbip, ip, #0movsip, ip, lsr #32 - PAGE_SHIFTbeq.cfu_0fupi/* * ip = max no. of bytes to copy before needing another "strt" insn */cmpr2, ipmovltip, r2subr2, r2, ipsubsip, ip, #32blt.cfu_0rem8lpPLD(pld[r1, #28])PLD(pld[r0, #28])PLD(subsip, ip, #64)PLD(blt.cfu_0cpynopld)PLD(pld[r1, #60])PLD(pld[r0, #60]).cfu_0cpy8lp:PLD(pld[r1, #92])PLD(pld[r0, #92]).cfu_0cpynopld:ldmiar1!, {r3 - r6}@ Shouldnt faultstmiar0!, {r3 - r6}ldmiar1!, {r3 - r6}@ Shouldnt faultsubsip, ip, #32stmiar0!, {r3 - r6}bpl.cfu_0cpy8lpPLD(cmnip, #64)PLD(bge.cfu_0cpynopld)PLD(addip, ip, #64).cfu_0rem8lp:cmnip, #16ldmgeiar1!, {r3 - r6}@ Shouldnt faultstmgeiar0!, {r3 - r6}tstip, #8ldmneiar1!, {r3 - r4}@ Shouldnt faultstmneiar0!, {r3 - r4}tstip, #4ldrnetr3, [r1], #4@ Shouldnt faultstrner3, [r0], #4andsip, ip, #3beq.cfu_0fupi.cfu_0nowords:teqip, #0beq.cfu_finished.cfu_nowords:cmpip, #2USER(ldrbtr3, [r1], #1)@ May faultstrbr3, [r0], #1USER(ldrgebtr3, [r1], #1)@ May faultstrgebr3, [r0], #1USER(ldrgtbtr3, [r1], #1)@ May faultstrgtbr3, [r0], #1b.cfu_finished.cfu_not_enough:movsip, r2bne.cfu_nowords.cfu_finished:movr0, #0addsp, sp, #8LOADREGS(fd,sp!,{r4 - r7, pc}).cfu_src_not_aligned:bicr1, r1, #3USER(ldrtr7, [r1], #4)@ May faultcmpip, #2bgt.cfu_3fupibeq.cfu_2fupi.cfu_1fupi:subsr2, r2, #4addmiip, r2, #4bmi.cfu_1nowordsmovr3, r7, pull #8USER(ldrtr7, [r1], #4)@ May faultorrr3, r3, r7, push #24strr3, [r0], #4movip, r1, lsl #32 - PAGE_SHIFTrsbip, ip, #0movsip, ip, lsr #32 - PAGE_SHIFTbeq.cfu_1fupicmpr2, ipmovltip, r2subr2, r2, ipsubsip, ip, #16blt.cfu_1rem8lpPLD(pld[r1, #12])PLD(pld[r0, #12])PLD(subsip, ip, #32)PLD(blt.cfu_1cpynopld)PLD(pld[r1, #28])PLD(pld[r0, #28]).cfu_1cpy8lp:PLD(pld[r1, #44])PLD(pld[r0, #44]).cfu_1cpynopld:movr3, r7, pull #8ldmiar1!, {r4 - r7}@ Shouldnt faultsubsip, ip, #16orrr3, r3, r4, push #24movr4, r4, pull #8orrr4, r4, r5, push #24movr5, r5, pull #8orrr5, r5, r6, push #24movr6, r6, pull #8orrr6, r6, r7, push #24stmiar0!, {r3 - r6}bpl.cfu_1cpy8lpPLD(cmnip, #32)PLD(bge.cfu_1cpynopld)PLD(addip, ip, #32).cfu_1rem8lp:tstip, #8movner3, r7, pull #8ldmneiar1!, {r4, r7}@ Shouldnt faultorrner3, r3, r4, push #24movner4, r4, pull #8orrner4, r4, r7, push #24stmneiar0!, {r3 - r4}tstip, #4movner3, r7, pull #8USER(ldrnetr7, [r1], #4)@ May faultorrner3, r3, r7, push #24strner3, [r0], #4andsip, ip, #3beq.cfu_1fupi.cfu_1nowords:movr3, r7, get_byte_1teqip, #0beq.cfu_finishedcmpip, #2strbr3, [r0], #1movger3, r7, get_byte_2strgebr3, [r0], #1movgtr3, r7, get_byte_3strgtbr3, [r0], #1b.cfu_finished.cfu_2fupi:subsr2, r2, #4addmiip, r2, #4bmi.cfu_2nowordsmovr3, r7, pull #16USER(ldrtr7, [r1], #4)@ May faultorrr3, r3, r7, push #16strr3, [r0], #4movip, r1, lsl #32 - PAGE_SHIFTrsbip, ip, #0movsip, ip, lsr #32 - PAGE_SHIFTbeq.cfu_2fupicmpr2, ipmovltip, r2subr2, r2, ipsubsip, ip, #16blt.cfu_2rem8lpPLD(pld[r1, #12])PLD(pld[r0, #12])PLD(subsip, ip, #32)PLD(blt.cfu_2cpynopld)PLD(pld[r1, #28])PLD(pld[r0, #28]).cfu_2cpy8lp:PLD(pld[r1, #44])PLD(pld[r0, #44]).cfu_2cpynopld:movr3, r7, pull #16ldmiar1!, {r4 - r7}@ Shouldnt faultsubsip, ip, #16orrr3, r3, r4, push #16movr4, r4, pull #16orrr4, r4, r5, push #16movr5, r5, pull #16orrr5, r5, r6, push #16movr6, r6, pull #16orrr6, r6, r7, push #16stmiar0!, {r3 - r6}bpl.cfu_2cpy8lpPLD(cmnip, #32)PLD(bge.cfu_2cpynopld)PLD(addip, ip, #32).cfu_2rem8lp:tstip, #8movner3, r7, pull #16ldmneiar1!, {r4, r7}@ Shouldnt faultorrner3, r3, r4, push #16movner4, r4, pull #16orrner4, r4, r7, push #16stmneiar0!, {r3 - r4}tstip, #4movner3, r7, pull #16USER(ldrnetr7, [r1], #4)@ May faultorrner3, r3, r7, push #16strner3, [r0], #4andsip, ip, #3beq.cfu_2fupi.cfu_2nowords:movr3, r7, get_byte_2teqip, #0beq.cfu_finishedcmpip, #2strbr3, [r0], #1movger3, r7, get_byte_3strgebr3, [r0], #1USER(ldrgtbtr3, [r1], #0)@ May faultstrgtbr3, [r0], #1b.cfu_finished.cfu_3fupi:subsr2, r2, #4addmiip, r2, #4bmi.cfu_3nowordsmovr3, r7, pull #24USER(ldrtr7, [r1], #4)@ May faultorrr3, r3, r7, push #8strr3, [r0], #4movip, r1, lsl #32 - PAGE_SHIFTrsbip, ip, #0movsip, ip, lsr #32 - PAGE_SHIFTbeq.cfu_3fupicmpr2, ipmovltip, r2subr2, r2, ipsubsip, ip, #16blt.cfu_3rem8lpPLD(pld[r1, #12])PLD(pld[r0, #12])PLD(subsip, ip, #32)PLD(blt.cfu_3cpynopld)PLD(pld[r1, #28])PLD(pld[r0, #28]).cfu_3cpy8lp:PLD(pld[r1, #44])PLD(pld[r0, #44]).cfu_3cpynopld:movr3, r7, pull #24ldmiar1!, {r4 - r7}@ Shouldnt faultorrr3, r3, r4, push #8movr4, r4, pull #24orrr4, r4, r5, push #8movr5, r5, pull #24orrr5, r5, r6, push #8movr6, r6, pull #24orrr6, r6, r7, push #8stmiar0!, {r3 - r6}subsip, ip, #16bpl.cfu_3cpy8lpPLD(cmnip, #32)PLD(bge.cfu_3cpynopld)PLD(addip, ip, #32).cfu_3rem8lp:tstip, #8movner3, r7, pull #24ldmneiar1!, {r4, r7}@ Shouldnt faultorrner3, r3, r4, push #8movner4, r4, pull #24orrner4, r4, r7, push #8stmneiar0!, {r3 - r4}tstip, #4movner3, r7, pull #24USER(ldrnetr7, [r1], #4)@ May faultorrner3, r3, r7, push #8strner3, [r0], #4andsip, ip, #3beq.cfu_3fupi.cfu_3nowords:movr3, r7, get_byte_3teqip, #0beq.cfu_finishedcmpip, #2strbr3, [r0], #1USER(ldrgebtr3, [r1], #1)@ May faultstrgebr3, [r0], #1USER(ldrgtbtr3, [r1], #1)@ May faultstrgtbr3, [r0], #1b.cfu_finished.section .fixup,"ax".align0/* * We took an exception. r0 contains a pointer to * the byte not copied. */9001:ldrr2, [sp], #4@ void *tosubr2, r0, r2@ bytes copiedldrr1, [sp], #4@ unsigned long countsubsr4, r1, r2@ bytes left to copymovner1, r4blne__memzeromovr0, r4LOADREGS(fd,sp!, {r4 - r7, pc}).previous我们将在另一篇博文中详细分析该函数。
- linux内核中的copy_to_user和copy_from_user(一)
- linux内核中的copy_to_user和copy_from_user(一)
- linux内核中的copy_to_user和copy_from_user(一)
- linux内核中的copy_to_user和copy_from_user(一)
- linux内核中的copy_to_user和copy_from_user(一)
- linux内核中的copy_to_user和copy_from_user(二)—图解__arch_copy_from_user
- linux内核学习---copy_to_user和copy_from_user
- 初步解析内核函数copy_to_user和copy_from_user
- linux copy_to_user和copy_from_user函数的使用说明
- linux copy_to_user() copy_from_user()
- linux copy_from_user/copy_to_user
- Linux copy_to_user、copy_from_user使用
- copy_to_user和copy_from_user函数
- copy_to_user 和 copy_from_user
- copy_to_user和copy_from_user
- copy_to_user和copy_from_user
- copy_to_user和copy_from_user
- copy_to_user和copy_from_user
- HDU 1116 Play on Words(有向图欧拉路)
- Mercurial项目管理实践
- 在VMWare Workstation8.0上使用ubuntu11安装和配置Hadoop群集环境02_配置虚拟机之间SSH无密码登录
- sleep 和 wait的区别
- ARM寻址方式及相关指令汇总
- linux内核中的copy_to_user和copy_from_user(一)
- post和get请求方式的区别
- makefile 笔记
- xendesktop 基于ip地址或主机名分配桌面
- Android零点一度的区别——Matrix
- 线程同步
- 国家利益未必就是民族利益
- 用 Java 技术创建 RESTful Web (服务 JAX-RS:一种更为简单、可移植性更好的替代方式)
- "棱镜门事件"的思考:黑客是什么,如何成为黑客?[更新:2015-05-09]