免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4538 | 回复: 9
打印 上一主题 下一主题

[讨论]实现异步IO机制? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-03-22 11:42 |只看该作者 |倒序浏览
20可用积分
最近异想天开,想做一个内核模块,以实现完全异步IO。(并没有什么特别的需求,仅仅是想做点东西,熟悉一下内核态的编程。)

linux内核提供了异步IO的接口,但是这并不是完全的异步IO,仅仅是提供了一种异步通知机制,通知fd可读、可写、或出错。而用户程序得到这个通知后,还需要调用read/write去同步完成读写。
而完全的异步IO应该是:用户程序调用read/write接口;不管指定的fd是否可读可写,系统调用直接返回;内核记录下read/write传入的参数(file,buffer,size)并完成实际的读写过程(可能需要等待),然后向用户程序发信号通知;用户程序得到信号通知后,直接使用buffer里面的数据即可。

按我的想法,实现这个完全异步IO的内核模块注册为一个cdev。
用户使用完全异步IO的方式如下:
1、打开这个设备,获得一个设备fd;
2、通过ioctl接口,将另一个实际需要读写的fd(记为fd2)“绑定”到这个设备fd上;
3、设置fd2的f_owner,指定异步通知的对象,并注册对应的信号处理过程;
4、对这个设备fd进行一次读写操作,读写操作不阻塞。用户程序在信号处理过程中处理fd的读写结果;

内核模块的实现如下:
1、init:创建一个内核线程,作为工作线程;
2、open:创建一个异步任务描述对象,存放在file->private_data;
3、ioctl:设置异步任务描述对象的属性,记录到fd2的file结构;
4、read/write:设置异步任务描述对象的属性,记录buffer/size,并向工作线程添加这个任务;
5、工作线程:通过调用对应的“绑定”的fd2的poll接口,判断其是否可读/可写。如果可读/可写,则调用vfs_read/vfs_write进行读写,然后发送信号通知;如果不可读/可写,工作线程挂起等待(类似sys_poll的做法);

大体的想法就是这样。但是其中有一点感觉很难实现:异步任务描述对象中保存的buffer地址是一个虚拟地址,这个地址仅仅在对应的进程上下文中才有效,在工作线程中是无效的,不能使用copy_to_user……
这个问题该如何解决呢?希望听听大家的见解,望不吝赐教~

最佳答案

查看完整内容

用户态传地址再在内核态转物理地址本身就不是个标准做法。通常做法是driver分配内存供用户态map。其实不用搞那么复杂,你可以在内核中分配一块和用户态大小一样的内存,先写到这里面,最后在copy到用户态传下来的地址中。

论坛徽章:
0
2 [报告]
发表于 2009-03-22 11:42 |只看该作者
原帖由 kouu 于 2009-3-24 15:19 发表


用户传入的buffer及size可能是跨page的,我现在的想法是使用get_user_pages()得到对应的page数组,然后使用vmap在内核空间映射成一个连续的地址。应该没什么问题吧?

用户态传地址再在内核态转物理地址本身就不是个标准做法。通常做法是driver分配内存供用户态map。
其实不用搞那么复杂,你可以在内核中分配一块和用户态大小一样的内存,先写到这里面,最后在copy到用户态传下来的地址中。

论坛徽章:
0
3 [报告]
发表于 2009-03-22 16:14 |只看该作者
原帖由 kouu 于 2009-3-22 11:42 发表
最近异想天开,想做一个内核模块,以实现完全异步IO。(并没有什么特别的需求,仅仅是想做点东西,熟悉一下内核态的编程。)

linux内核提供了异步IO的接口,但是这并不是完全的异步IO,仅仅是提供了一种异步 ...

虽然这本质上和目前Linux的AIO有什么区别,不过LZ愿意自己写着玩也是很不错的。
你那个问题容易解决,你把传下来的虚拟地址转化为物理地址,再自己在驱动里面map到一个虚拟地址,直接写到这个虚拟地址就可以了

论坛徽章:
0
4 [报告]
发表于 2009-03-22 17:34 |只看该作者
原帖由 zx_wing 于 2009-3-22 16:14 发表

虽然这本质上和目前Linux的AIO有什么区别,不过LZ愿意自己写着玩也是很不错的。
你那个问题容易解决,你把传下来的虚拟地址转化为物理地址,再自己在驱动里面map到一个虚拟地址,直接写到这个虚拟地址就可以了


之前没有用过AIO(早一点的书籍里面也没提到过这东西,应该是2.6内核才有的吧~),不过看了一下介绍,确实已经实现了我想实现的功能。感谢指点~

另外还有几个问题想请教一下:
1、AIO应该是需要file->f_op里面有定义aio_XXX才能用的吧?是否有些驱动程序并不会提供aio_XXX接口?
2、你说的那个方法,应该先得到用户传入的buffer对应的page,然后kmap一下建立一个固定映射吧。但是kmap得到的虚拟地址能否作为vfs_read/vfs_write的参数呢(也即是说,这个地址能否作为copy_to_user/copy_from_user中的from参数)?

论坛徽章:
0
5 [报告]
发表于 2009-03-22 18:20 |只看该作者
原帖由 kouu 于 2009-3-22 17:34 发表

但是kmap得到的虚拟地址能否作为vfs_read/vfs_write的参数呢(也即是说,这个地址能否作为copy_to_user/copy_from_user中的from参数)?


看了一下copy_to_user/copy_from_user,里面并不会去检查from地址是否合法(合法性是由MMU来检查的,内核只管捕捉异常);
而在vfs_read/vfs_write时,会调用access_ok来检查传入的buffer地址。这里只是检查buffer是否在进程的addr_limit以内吧;
所以,kmap得到的虚拟地址可以作为vfs_read/vfs_write的参数。
请问一下这个理解是否有误?

论坛徽章:
0
6 [报告]
发表于 2009-03-22 23:18 |只看该作者
lz告诉你吧,netlink就是实现的你要的完全异步的通信的

论坛徽章:
0
7 [报告]
发表于 2009-03-22 23:22 |只看该作者
http://www.ibm.com/developerworks/cn/linux/l-connector/

看看这篇介绍netlink的文章

论坛徽章:
0
8 [报告]
发表于 2009-03-23 18:07 |只看该作者
如何通过用户传入的虚拟地址获取对应的page呢?可能这个虚拟地址在传入时尚未被映射……

另外,大概看了一下aio的实现。好像是记录下用户进程的mm,然后在内核线程里面use_mm()一下……也就是存在一次页表的切换。


原帖由 emmoblin 于 2009-3-22 23:18 发表
lz告诉你吧,netlink就是实现的你要的完全异步的通信的

我所讨论的异步机制应该说是接口层面的吧,比如类似read/write这样的接口;
而据我所知,netlink应该是实现层面的了,对netlink的操作还需要使用到类似read/write这样的接口(比如内核通过netlink发出一个信息给用户进程,用户进程还需要调用IO接口去接收这个信息);
不是这样的么?

[ 本帖最后由 kouu 于 2009-3-23 18:21 编辑 ]

论坛徽章:
0
9 [报告]
发表于 2009-03-23 21:50 |只看该作者
原帖由 kouu 于 2009-3-23 18:07 发表
如何通过用户传入的虚拟地址获取对应的page呢?可能这个虚拟地址在传入时尚未被映射……

另外,大概看了一下aio的实现。好像是记录下用户进程的mm,然后在内核线程里面use_mm()一下……也就是存在一次页表的 ...

拿到地址后你可以先copy_to_user一下保证分配页面。
在内核中Mapping后的地址直接用memcpy写就可以了。map后的地址可以被内核中其它函数直接使用

论坛徽章:
0
10 [报告]
发表于 2009-03-24 15:19 |只看该作者
原帖由 zx_wing 于 2009-3-23 21:50 发表

拿到地址后你可以先copy_to_user一下保证分配页面。
在内核中Mapping后的地址直接用memcpy写就可以了。map后的地址可以被内核中其它函数直接使用


用户传入的buffer及size可能是跨page的,我现在的想法是使用get_user_pages()得到对应的page数组,然后使用vmap在内核空间映射成一个连续的地址。应该没什么问题吧?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP