30 | 真正的大杀器:异步I/O探索
30 | 真正的大杀器:异步I/O探索
讲述:冯永吉
时长10:57大小15.05M
阻塞 / 非阻塞 VS 同步 / 异步
aio_read 和 aio_write 的用法
Linux 下 socket 套接字的异步支持
Windows 下的 IOCP 和 Proactor 模式
总结
思考题
赞 9
提建议
精选留言(32)
- fackgc172019-10-16Linux 的 AIO 机制可能后面逐渐不用了,可以关注 5.1 的 io_uring 机制,大杀器
作者回复: 赞,学习了。
29 - 钱2019-12-01记一下自己对阻塞/非阻塞/同步/异步的理解 1:阻塞/非阻塞——是站在调用者(客户端或请求方)的角度来说的,如果调用者要数据,此时服务者没准备好,调用者不用傻等,那就是非阻塞的,否则就是阻塞的 2:同步/异步——是站在服务者(服务端或响应端)的角度来说的,如果服务者不会主动给请求者它想要的数据,那就是同步的,否则就是异步的 3:两个位置每个位置有两种状态,总共四种状态——同步阻塞/同步非阻塞/异步阻塞(不搭)/异步非阻塞 ,其中同步阻塞和异步非阻塞是很搭的相互成就,异步阻塞是非常不搭的相互矛盾,同步非阻塞有一点怪,它通常就是靠不断的轮询来实现的,服务者不会主动把准备好的数据发给调用者,调用者也不会等着服务者把数据准备好再返回。 4:导致出现这种现象的根本原因,我觉得是数据的读取是一个慢动作,数据不管从磁盘中拿出来还是放入到磁盘中,相对内存操作相对CPU执行命令的操作速度是非常慢的,这种速度差必然导致要么等一等,要么不断的来瞧瞧,要么准备好了送过去,这三种解决此问题的思路。展开共 1 条评论12
- 进击的巨人2020-11-15非常好的总结:Reactor 模式是基于待完成的 I/O 事件,而 Proactor 模式则是基于已完成的 I/O 事件。
作者回复: 都是前人栽树,后人乘凉 )
8 - TinyCalf2020-11-10老师我有些想法不知道正不正确: 其实我用非阻塞IO,自己写代码把数据拷贝过程和业务处理过程分离到多个线程,也能实现代码层面的异步,而操作系统提供的异步IO只不过是把这个过程转到内核态去完成;可能内核处理这些逻辑比我们自己写的代码效率要高些,但是绝对不会像多路复用接口一样带来巨大的效率提升;nodejs好像就是这样,用非阻塞IO+libuv实现的eventloop来实现代码层面的异步,但并没有使用异步IO接口展开
作者回复: Linux系统提供的异步I/O还停留在很浅的阶段,所以,现实的做法都是多路复用+非阻塞I/O来完成你说的"代码层面的异步",这已经足够高效和有用了。
6 - 马不停蹄2019-11-13异步I/O就相当于当数据真正到达或者说应用程序读取完数据了通过注册的回调函数通知你去处理,用 netty 是这样的,但是老师有个问题一直不明白:netty 并没有用异步I/O,而是基于(多路复用+非阻塞I/O)+ Future 实现了异步非阻塞吗?
作者回复: Netty用的就是epoll,基于多路复用+非阻塞I/O,至于Future,只是Java里包装异步调用的一种方式,并不是真正的异步 I/O。
6 - 传说中的成大大2019-10-16看第二遍理解了reactor和proactor的区别前者是同步 有消息到达时调用应用程序的回调,应用程序自己调用read 同步取得数据,而后者是内核异步数据读取完成之后才调用应用程序的回调
作者回复: 我理解Linux下标榜的proactor其实都是伪的。
4 - 传说中的成大大2019-10-16而突然又理解到了同步i/o和异步i/o的问题 比如我调用read函数 在read函数返回之前数据被拷贝到缓冲区这个过程就是同步i/o的操作 像后面的aio系列函数 是在函数调用后 内核把数据拷贝到应用层缓冲区 这个就叫异步
作者回复: 你真的悟道了,哈哈:)
3 - null2021-04-14还是没太理解阻塞和同步之间的区别 1. 阻塞:只是针对线程挂起这一场景,线程无法获取 cpu 时间片,无法处理其他逻辑。 2. 非阻塞:线程正常获取 cpu 时间片,正常运行,可以不断轮询 read 函数,快速返回,同时也可以处理其他逻辑。 结论:阻塞和非阻塞的区别,只是针对线程是否挂起(即能否获取 cpu 时间片)。 同步:线程自己读取数据,不断地从内核缓冲区读取到应用程序缓冲区,直到读取所有数据。读数据期间线程忙个不停,疲于奔命。(读数据这期间线程脱身去无法处理其他逻辑) 异步:内核把所有数据写到应用程序缓冲区,再通知应用程序处理。在通知之前,应用程序都可以去处理其他逻辑。 结论:同步和异步的区别,只是针对谁将数据读到应用程序缓冲区。 自己一直混淆阻塞和同步的概念,应该都是被“线程无法处理其他逻辑”所迷惑,感觉两者的表现都是一样的,都是无法处理其他逻辑,因此将这两个概念混为一谈。 老师,请问一下,我对阻塞/非阻塞和同步/异步的理解,并且相关的结论,是否正确?谢谢老师!!展开
作者回复: 我觉得你get到了。
共 3 条评论3 - 土豆牛肉2019-11-20既然Windows有iocp,是不是可以说Windows更适合运行网络服务器呢
作者回复: 不是哦,只是设计上各有千秋,现在互联网上跑的最多的还是Linux,就连微软,也在积极拥抱Linux。
2 - 传说中的成大大2019-10-19issue和mr是啥意思啊,没接触到过呢!
作者回复: issue是提一个问题到github上,mr是看哪里有问题直接改代码,提一个merge request过来,我直接merge到master把问题解决了
2 - 菠萝power2021-03-21老师好。同步read的时候,内核把数据拷贝到应用程序这个时间段消耗的是应用程序的时间片吗?
作者回复: 这个看你的理解,如果你认为是同步read调用的发起,导致了应用程序进入休眠,等待系统把数据拷贝完,从应用程序角度来看,好像是应用程序自己把时间片拱手让人,从而消耗了自己的时间,这个理解是对的。
1 - Richard2020-03-16针对老师提出的第二个问题也是我一直思考的:异步io的应用场景。像老师给的例子代码发出读写后依然循环等待结果,这断然不是异步io的使用场景,还不如用同步io来节省CPU呢,所以异步io的使用场景有哪些?
作者回复: Linux下确实没有非常好的异步I/O场景,文章中给出了Windows下的IOCP,倒是一个真实的异步I/O的场景。
1 - 传说中的成大大2019-10-17在poll-server-onethread程序中 onMessage回调里面调用 char *run_cmd(char *cmd) { char *data = malloc(16384); bzero(data, sizeof(data)); FILE *fdp; const int max_buffer = 256; char buffer[max_buffer]; fdp = popen(cmd, "r"); char *data_index = data; if (fdp) { while (!feof(fdp)) { if (fgets(buffer, max_buffer, fdp) != NULL) { int len = strlen(buffer); memcpy(data_index, buffer, len); data_index += len; } } pclose(fdp); } return data; } 总是提示 get message from tcp connection connection-7 ls : not found 这就让我很蛋疼了,百度了半天没解决到展开
作者回复: 从onMessage如何调到run_cmd的?
共 2 条评论1 - 程序水果宝2019-10-16看了最近几篇文章以后个人感觉应该把反应堆、epoll、异步和同步的函数列出来配合着它们的功能讲,很有可能不懂的地方都在那些封装的函数里面,像main函数里面的内容反而给出链接加注释就可以了,这样可能会让人的理解更加深刻一些。还有实验结果也不用列这么多,这些完全可以由自己去实验。
作者回复: 感谢你的建议。时间有限,做出来的内容可能没有办法满足所有人的需求。在第四篇里可能会解答你的大部分疑惑,如果有进一步的问题,我可以在答疑中统一回复,解答大家的疑惑。
共 2 条评论1 - ano2022-08-24 来自北京之前的那个留言描述不对!正确的应该是下面这样: "非阻塞IO,最后一次 read 调用,获取数据的过程,是一个同步的过程。这里的同步指的是内核区域的数据拷贝到缓冲区的这个过程。” 这里说获取数据是一个同步的过程,那在已经 read ready的非阻塞套接字上调用 recv 的时候,就不会立即返回了?而是要等到数据完全拷贝完成后,才会返回?是这个意思么?
- gecko2021-12-03请教老师,编译这个报错怎么解决。 git clone https://github.com/froghui/yolanda.git cd yolanda mkdir build cd build/ cmake ../ make 如下输出 [ 97%] Linking C executable ../bin/aio01 CMakeFiles/aio01.dir/aio01.c.o: In function `main': aio01.c:(.text+0x19b): undefined reference to `aio_write' aio01.c:(.text+0x1f4): undefined reference to `aio_error' aio01.c:(.text+0x208): undefined reference to `aio_error' aio01.c:(.text+0x21d): undefined reference to `aio_return' aio01.c:(.text+0x329): undefined reference to `aio_read' aio01.c:(.text+0x379): undefined reference to `aio_error' aio01.c:(.text+0x38d): undefined reference to `aio_return' collect2: error: ld returned 1 exit status chap-30/CMakeFiles/aio01.dir/build.make:95: recipe for target 'bin/aio01' failed make[2]: *** [bin/aio01] Error 1 CMakeFiles/Makefile2:2286: recipe for target 'chap-30/CMakeFiles/aio01.dir/all' failed make[1]: *** [chap-30/CMakeFiles/aio01.dir/all] Error 2 Makefile:129: recipe for target 'all' failed make: *** [all] Error 2 root@ubuntu-192-168-1-182:/tmp/yolanda/build#展开
作者回复: 加一下 rt这个动态库 在CMakeLists.txt里面添加: target_link_libraries(aio01 yolanda rt)
- schbxg2021-08-21如果使用g++编译,52行的cb结构需要初始化一下,不然下边aio_read的时候会报无效参数的错误。
作者回复: OK。
- vv_test2021-06-29老师您好,为什么异步io有好处,linux 也迟迟没有内核级别的支持套接字异步io。是说c10k已经可以使用前面的知道方案解决了,即使内核级别支持(我理解内核级别也是要消耗资源),想要再高的并发,瓶颈不在这了吗?
作者回复: 我理解是已经足够用了,尽管各种学术研究层出不穷,异步I/O这个领域也一直是不温不火。
- night2021-02-25Reactor 模式是基于待完成的 I/O 事件,而 Proactor 模式则是基于已完成的 I/O 事件。在买书的例子下,就是 Reactor: 书店通知你书到店里了,可以来书店取了 Proactor:书店把书寄到了你之前留的地址上了,可以直接签收
作者回复: 我感觉有点不太对,Proactor是windows里IOCP的抽象,你可以搜一下。
- 梁✨飞2021-01-06老师你好,假如用堵塞的方式执行 read 操作,缓冲区可以有足够的数据,read 方法马上返回,那么线程的状态还会变为堵塞状态吗?这种立即返回read的情况线程会丢失 CPU 控制权吗?
作者回复: 线程的状态是由我们程序控制的,这里的问题是读出数据之后线程接下来会去干啥。如果你继续做一段事情,不会丧失控制权。