关于内核与用户地址空间的问题,期待解惑
在写字符驱动时,发现一个不能理解的问题:
实现读写时,使用copy_to_user或copy_from_user来在内核与用户之间传递参数,但是我们可以在ioctl中用第三个参数传递一个结构体给内核
问题是:在ioctl中使用的地址却是用户传递过来的地址(是用户将地址以一个长整型值传递过来的,用ioctl函数中的第三个参数),并且ioctl函数用这个地址直接去找对应的结构体,居然能找到用户赋值过的那个结构体~感觉这个有点奇怪了,内核怎么会认识用户传过来的地址呢(内核地址空间跟用户的是不一样的嘛)?那如果内核能直接使用用户传递的地址的话,我们干吗还去用copy_to_user这样的函数呢?直接传过来不就好了?
想不明白了,请各位指教下下~~
3Q~
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
你要明白,ioctl是一个系统调用,系统调用是用户空间进入内核空间的一个入口。应用程序的write函数实际上也是把用户空间的数据传到内核,而底层的处理实际上是调用了copy_from_user来实现的。ioctl只是提供一个操作接口,不适合于传输大量数据到内核空间。最终内核空间ioctl是怎么实现的,取决于你写的驱动程序。
没错,好象系统调用就是通过copy_from_user这样的函数来传递参数的吧。
但是我不能理解的一点是:
我通过ioctl从用户传递一个结构体的地址(注意这里参数传的地址是用户空间的逻辑地址,如0xb******)到内核,在驱动程序中直接使用这个参数作为起始地址去读取结构体里面的内容,这样为什么能读到正确的内容呢?我确实在驱动程序中把读取结构体内容的起始地址打印出来,居然是跟用户传递时的地址值一样,也就是0xb*******这样的地址。
按理来说内核应该不认识像0xb******这样的逻辑地址啊,内核中的逻辑地址应该是0xc*******的撒。
逻辑地址到物理地址的转换是在内核中完成的,并不是在内核空间就一定只能访问0xc0000000到0xffffffff之间的线性地址。而在用户空间,当然是无法访问oxc0000000以上的地址空间的。
所以,你说的内核不认识0xb******的地址这个认识是错误的。之所以标识0xc0000000到0xffffffff之间的线性地址映射为内核空间,是为了防止用户空间的程序访问这个范围的地址,而并不是限定给内核仅仅这段地址。分页机制把逻辑地址转换为实际的物理地址,是在内核中完成的,内核能处理这个逻辑地址(4GB范围的地址),这就说明了,内核空间的寻址范围就是4G。
PS:上面说的逻辑地址和线性地址没有加区分,因为linux中是一致的。
[ 本帖最后由 dreamice 于 2008-10-7 22:06 编辑 ]
原来是这样啊!就是说内核是0~4G的逻辑地址都可以使用的咯~
那这样的话,我觉得很多地方我们都可以不管内核空间和用户空间啊,也不需要用copy_from_user和copy_to_user来传递参数值了(当然,也许像你说的那样,这种方式不适合传大量数据;但内核直接使用用户地址这种方式确实是没有问题的?),我改了下驱动,在read和write中也不用copy_from_user这样的API来做,而是直接用用户传过来的地址,结果也是对的!
我想我应该具体了解下copy_from_user这样的API到底做了什么事情才行~
你好好看一下copy_from(to)_user的实现,不过里面有很大一段晦涩的汇编代码。
地址空间的划分是为了区分内核空间和用户空间,并防止越界访问。虽然寻址能力是4G,但数据的传输不能简单的在内核空间就访问用户空间,这就是copy_from(to)_user存在的原因和价值。你如果在read和write中也不用copy_from_user这样的API来做可能会出现很严重的问题。
copy_from_user的目的是防止用户程序欺骗内核,将一个非法的地址传进去,如果没有它,这一非法地址就检测不到,内和就会访问这个地址指向的数据。因为在内核中访问任何地址都没有保护,如果不幸访问一个错误的内存地址会搞死内核或发生更严重的问题
[ 本帖最后由 dreamice 于 2008-10-8 10:27 编辑 ]
回复 6# dreamice
直接使用用户空间的地址,会出错的,如果没出错,只是好运,ioctl中使用了传过来的结构体,应该也是不妥的,大牛请指点。
不能使用用户空间的地址,这个只是一个虚拟地址,最终需要映射到内核空间。用户地址空间和内核地址空间是相对独立的。