linux内核中的copy_to_user和copy_from_user(一)
1.2.__arch_copy_from_user
在深入讲解之前,我们先想一个问题:为什么要使用copy_from_user函数???理论上,内核空间可以直接使用用户空间传过来的指针,即使要做数据拷贝的动作,也可以直接使用memcpy,事实上,在没有MMU的体系架构上,copy_form_user最终的实现就是利用了memcpy。但对于大多数有MMU的平台,情况就有了一些变化:用户空间传过来的指针是在虚拟地址空间上的,它指向的虚拟地址空间很可能还没有真正映射到实际的物理页面上。但这又能怎样呢?缺页导致的异常会透明的被内核予以修复(为缺页的地址空间提交新的物理页面),访问到缺页的指令会继续运行仿佛什么都没有发生一样。但这只是用户空间缺页异常的行为,在内核空间这样却因一场必须被显示的修复,这是由内核提供的缺页异常处理函数的设计模式决定的,其背后的思想后:在内核态中,如果程序试图访问一个尚未提交物理页面的用户空间地址,内核必须对此保持警惕而不能像用户空间那样毫无察觉。
如果内核访问一个尚未被提交物理页面的空间,将产生缺页异常,内核会调用do_page_fault,因为异常发生在内核空间,do_page_fault将调用search_exception_tables在“ __ex_table”中查找异常指令的修复指令,在__arch_copy_from_user函数中经常使用USER宏,这个宏中了定义了“__ex_table”section。
linux/include/asm-arm/assembler.h
[plain]view plaincopyprint?
- #defineUSER(x...)\
- 9999:x;\
- .section__ex_table,"a";\
- .align3;\
- .long9999b,9001f;\
- .previous
该定义中有如下数据;
[plain]view plaincopyprint?
- .long9999b,9001f;
如果在驱动程序中不使用copy_from_user而用memcpy来代替,对于上述的情形会产生什么结果呢?当标号9999出发生缺页异常时,系统在“__ex_table”section总将找不到修复地址,因为memcpy没有像copy_from_user那样定义一个“__ex_table”section,此时do_page_fault将通过no_context函数产生Oops。极有可能会看到类似如下信息:
Unable to handle kernel NULL pointer dereference at virtual address 00000fe0
所有为了确保设备驱动程序的安全,应该使用copy_from_user函数而不是memcpy。
下面我们深入分析__arch_copy_from_user函数的实现,该函数是用汇编实现的,定义在linux/arch/arm/lib/uaccess.S文件中。[plain]view plaincopyprint?
- /*Prototype:unsignedlong__arch_copy_from_user(void*to,constvoid*from,unsignedlongn);
- *Purpose:copyablockfromusermemorytokernelmemory
- *Params:to-kernelmemory
- *:from-usermemory
- *:n-numberofbytestocopy
- *Returns:NumberofbytesNOTcopied.
- */
- .cfu_dest_not_aligned:
- rsbip,ip,#4
- cmpip,#2
- USER(ldrbtr3,[r1],#1)@Mayfault
- strbr3,[r0],#1
- USER(ldrgebtr3,[r1],#1)@Mayfault
- strgebr3,[r0],#1
- USER(ldrgtbtr3,[r1],#1)@Mayfault
- strgtbr3,[r0],#1
- subr2,r2,ip
- b.cfu_dest_aligned
- ENTRY(__arch_copy_from_user)
- stmfdsp!,{r0,r2,r4-r7,lr}
- cmpr2,#4
- blt.cfu_not_enough
- PLD(pld[r1,#0])
- PLD(pld[r0,#0])
- andsip,r0,#3
- bne.cfu_dest_not_aligned
- .cfu_dest_aligned:
- andsip,r1,#3
- bne.cfu_src_not_aligned
- /*
- *Seeingastherehastobeatleast8bytestocopy,wecan
- *copyoneword,andforceauser-modepagefault...
- */
- .cfu_0fupi:subsr2,r2,#4
- addmiip,r2,#4
- bmi.cfu_0nowords
- USER(ldrtr3,[r1],#4)
- strr3,[r0],#4
- movip,r1,lsl#32-PAGE_SHIFT@Oneachpage,useald/st??tinstruction
- rsbip,ip,#0
- movsip,ip,lsr#32-PAGE_SHIFT
- beq.cfu_0fupi
- /*
- *ip=maxno.ofbytestocopybeforeneedinganother"strt"insn
- */
- cmpr2,ip
- movltip,r2
- subr2,r2,ip
- subsip,ip,#32
- blt.cfu_0rem8lp
- PLD(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}@Shouldntfault
- stmiar0!,{r3-r6}
- ldmiar1!,{r3-r6}@Shouldntfault
- subsip,ip,#32
- stmiar0!,{r3-r6}
- bpl.cfu_0cpy8lp
- PLD(cmnip,#64)
- PLD(bge.cfu_0cpynopld)
- PLD(addip,ip,#64)
- .cfu_0rem8lp:cmnip,#16
- ldmgeiar1!,{r3-r6}@Shouldntfault
- stmgeiar0!,{r3-r6}
- tstip,#8
- ldmneiar1!,{r3-r4}@Shouldntfault
- stmneiar0!,{r3-r4}
- tstip,
linux内核中copy_to_usercopy_from_use 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)