




已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
在内核的学习中会遇到很多挺有意思的函数,而且能沿着一个函数扯出来很多个相关的函数。copy_to_user和copy_from_user就是在进行驱动相关程序设计的时候,要经常遇到的两个函数。由于内核空间与用户空间的内存不能直接互访,因此借助函数copy_to_user()完成用户空间到内核空间的复制,函数copy_from_user()完成内核空间到用户空间的复制。下面我们来仔细的理一下这两个函数的来龙去脉。首先,我们来看一下这两个函数的在源码文件中是如何定义的:/arch/i386/lib/usercopy.cunsigned longcopy_to_user(void _user *to, const void *from, unsigned long n) might_sleep(); BUG_ON(long) n 0); if (access_ok(VERIFY_WRITE, to, n) n = _copy_to_user(to, from, n); return n;EXPORT_SYMBOL(copy_to_user);从注释中就可以看出,这个函数的主要作用就是从内核空间拷贝一块儿数据到用户空间,由于这个函数有可能睡眠,所以只能用于用户空间。它有如下三个参数, To 目标地址,这个地址是用户空间的地址; From 源地址,这个地址是内核空间的地址; N 将要拷贝的数据的字节数。如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数。以上是对函数的一些说明,接下来让我们看看这个函数的内部面目:参数to的时候有个_user限定,这个在/include/linux/compiler.h中有如下定义:# define _user _attribute_(noderef, address_space(1)表示这是一个用户空间的地址,即其指向的为用户空间的内存大家可能对这个_attribute_感到比较迷惑,不过没关系,google一下嘛_attribute_是gnu c编译器的一个功能,它用来让开发者使用此功能给所声明的函数或者变量附加一个属性,以方便编译器进行错误检查,其实就是一个内核检查器。具体可以参考如下:/techtips/gnu-c-attributes.html接下来我们看一下might_sleep();它有两个实现版本,debug版本和非debug版本:在debug版本中,在有可能引起sleep的函数中会给出相应的提示,如果是在原子的上下文中执行,则会打印出栈跟踪的信息,这是通过_might_sleep(_FILE_, _LINE_);函数来实现的,并且接着调用might_resched()函数进行重新调度。在非debug版本中直接调用might_resched()函数进行重新调度。其实现方式为,在/ include/linux/kernel.h中:#ifdef CONFIG_DEBUG_SPINLOCK_SLEEPvoid _might_sleep(char *file, int line);# define might_sleep() do _might_sleep(_FILE_, _LINE_); might_resched(); while (0)#else# define might_sleep() do might_resched(); while (0)#endif接下来是一个检查参数合法性的宏:BUG_ON(long) n addr_limit.seg); flag; )其实现的功能为:(u33)addr + (u33)size = (u33)current-addr_limit.seg 判断上式是否成立,若不成立则表示地址有效,返回零;否则返回非零接下来的这个函数才是最重要的函数,它实现了拷贝的工作: _copy_to_user(to, from, n)其实现方式如下(在/include/asm-i386/uaccess.h中):static _always_inline unsigned long _must_check_copy_to_user(void _user *to, const void *from, unsigned long n) might_sleep(); return _copy_to_user_inatomic(to, from, n);有一个_always_inline宏,其内容就是inline,一个_must_check,其内容是在gcc3和gcc4版本里为_attribute_(warn_unused_result)其中might_sleep同上面_user时候的注释。最终调用的是_copy_to_user_inatomic(to, from, n)来完成拷贝工作的,此函数的实现如下(在/include/asm-i386/uaccess.h中):static _always_inline unsigned long _must_check_copy_to_user_inatomic(void _user *to, const void *from, unsigned long n) if (_builtin_constant_p(n) unsigned long ret; switch (n) case 1: _put_user_size(*(u8 *)from, (u8 _user *)to, 1, ret, 1); return ret; case 2: _put_user_size(*(u16 *)from, (u16 _user *)to, 2, ret, 2); return ret; case 4: _put_user_size(*(u32 *)from, (u32 _user *)to, 4, ret, 4); return ret; return _copy_to_user_ll(to, from, n);其中_builtin_constant_p(n)为gcc的内建函数,_builtin_constant_p用于判断一个值是否为编译时常熟,如果参数n的值为常数,函数返回1,否则返回0。很多计算或操作在参数为常数时有更优化的实现,在 GNU C 中用上面的方法可以根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在参数是常数时编译出最优化的代码。如果n为常数1、2或者4,就会选择某个swith来执行拷贝动作,拷贝是通过如下函数来实现的(在/include/asm-i386/uaccess.h中):#ifdef CONFIG_X86_WP_WORKS_OK#define _put_user_size(x,ptr,size,retval,errret) do retval = 0; _chk_user_ptr(ptr); switch (size) case 1: _put_user_asm(x,ptr,retval,b,b,iq,errret);break; case 2: _put_user_asm(x,ptr,retval,w,w,ir,errret);break; case 4: _put_user_asm(x,ptr,retval,l,ir,errret); break; case 8: _put_user_u64(_typeof_(*ptr)(x),ptr,retval); break; default: _put_user_bad(); while (0)#else#define _put_user_size(x,ptr,size,retval,errret) do _typeof_(*(ptr) _pus_tmp = x; retval = 0; if(unlikely(_copy_to_user_ll(ptr, &_pus_tmp, size) != 0) retval = errret; while (0)#endif其中_put_user_asm为一个宏,拷贝工作是通过如下的内联汇编来实现的(在/include/asm-i386/uaccess.h中):#define _put_user_asm(x, addr, err, itype, rtype, ltype, errret) _asm_ _volatile_( 1: movitype %rtype1,%2n 2:n .section .fixup,axn 3: movl %3,%0n jmp 2bn .previousn .section _ex_table,an .align 4n .long 1b,3bn .previous : =r(err) : ltype (x), m(_m(addr), i(errret), 0(err)以上这两个函数是为了在拷贝小字节数据比如char/int等数据的时候考虑到效率来实现小数据拷贝。而若n不是如上所说的常数,则进行数据块区域拷贝,其实现如下(/arch/i386/lib/usercopy.c):unsigned long _copy_to_user_ll(void _user *to, const void *from, unsigned long n) BUG_ON(long) n 0);#ifndef CONFIG_X86_WP_WORKS_OK if (unlikely(boot_cpu_data.wp_works_ok = 0) & (unsigned long )to) TASK_SIZE) /* * CPU does not honor the WP bit when writing * from supervisory mode, and due to preemption or SMP, * the page tables can change at any time. * Do it manually. Manfred */ while (n) unsigned long offset = (unsigned long)to)%PAGE_SIZE; unsigned long len = PAGE_SIZE - offset; int retval; struct page *pg; void *maddr; if (len n) len = n;survive: down_read(¤t-mm-mmap_sem); retval = get_user_pages(current, current-mm, (unsigned long )to, 1, 1, 0, &pg, NULL); if (retval = -ENOMEM & current-pid = 1) up_read(¤t-mm-mmap_sem); blk_congestion_wait(WRITE, HZ/50); goto survive; if (retval != 1) up_read(¤t-mm-mmap_sem); break; maddr = kmap_atomic(pg, KM_USER0); memcpy(maddr + offset, from, len); kunmap_atomic(maddr, KM_USER0); set_page_dirty_lock(pg); put_page(pg); up_read(¤t-mm-mmap_sem); from += len; to += len; n -= len; return n; #endif if (movsl_is_ok(to, from, n) _copy_user(to, from, n); else n = _copy_user_intel(to, from, n); return n;EXPORT_SYMBOL(_copy_to_user_ll);下面是copy_from_user函数的实现:unsigned longcopy_from_user(void *to, const void _user *from, unsigned long n) mi
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 医院公卫培训知识题库课件
- 医疗纪检课件
- 创城业务知识培训课件
- 工程护航就业方案(3篇)
- 2025年县以低碳发展驱动产业绿色变革调研报告
- 2025年文化创意产业发展与IP保护试题及答案
- 2025年网络营销专家职业技能认证考试试题及答案
- 2025年ZRO2陶瓷磨介项目发展计划
- 青岛城阳区中考数学试卷
- 历界温州市中考数学试卷
- 2025-2030中国透水砖市场深度调查研究报告
- 建筑工程施工安全监督审查手续
- 小儿荨麻疹的护理查房
- 生产经营单位主要负责人和安全管理人员安全培训教材
- 劳务派遣劳务外包项目方案投标文件(技术方案)
- 空雨伞管理法
- 甲状腺围手术期病人的护理
- 水电站班组长管理培训
- 汽车4S店二手车收购流程
- 中国、世界矢量地图素材(详细到省市、能编辑)
- 西安交通大学《临床流行病学》2023-2024学年第一学期期末试卷
评论
0/150
提交评论