IO 多路复用快的原因具体是什么

2018-11-25 23:01:32 +08:00
 cc959798

比如 epoll,能管理大量的连接 1.但是有个疑问是,这些连接的处理也是处理器来操作的,也是要耗费大量时间的,为什么使用 epoll 来管理就快了呢,一个个处理不是也是一样的吗? 2.文件描述符 fd 的数据准备好 具体是指什么?磁盘?网络? 3.另外 IO 多路复用是数据的准备操作是在一个线程里处理的吗?还是说 fd 被丢给了其他线程,然后主线程等着数据准备

3623 次点击
所在节点    问与答
14 条回复
ech0x
2018-11-25 23:03:53 +08:00
没有创建进程 /线程开销
gamexg
2018-11-25 23:36:47 +08:00
特点不是快,而是可以节省系统资源。

最基础的同步阻塞模式意味着随着连接数增加需要增加系统线程,而系统线程成本比较高。

非阻塞模式等于不管有没有数据不断遍历所有 socket,这会浪费 cpu。

epoll 则是非阻塞的优化版本,不用自己去挨个尝试去读取所有 socket 看看哪个可用,系统在 socket 可用时通知应用层。
lhx2008
2018-11-25 23:51:55 +08:00
1.原来 IO 没返回要开线程等着,现在线程可以复用了
2.应该是系统的 IO 缓冲区中,对方已经传了一些数据到内存了(不确定,看看楼下大神怎么说),线程可以来拿了
3.应该在系统内核中的线程管理 ,具体原理我也不太清楚
kran
2018-11-26 00:55:15 +08:00
不恰当类比一下,吞吐能力上去了,但单个响应不一定快的。
secondwtq
2018-11-26 00:57:17 +08:00
个人意见:不知道楼主上没上过体系结构的课,我们有一部分是讲 IO 的,当然是硬件层面,先讲了 busy wait 模式,然后说这样会阻塞 CPU 不实用,然后讲了 interrupt 和 DMA 模式,这个是现代 CPU 一直在用的

而在学写 server 时(同样处理 IO 问题),会先学写单线程单请求的,然后是多线程每个线程一个请求的,然后是多路复用的

有没有发现区别 ... 硬件上处理 IO 根本就没有线程的概念,因为主流体系架构使用的 IO 控制机制压根就跟线程没关系(当然 interrupt handler 执行之前会触发 context switch,不过线程建立在此基础之上),用线程来处理 IO 问题的抽象是不合适且不必要的

你可以粗暴的认多路复用是系统提供给用户操作更底层 IO 处理机制的最低成本的一种抽象
cc959798
2018-11-26 09:23:25 +08:00
@gamexg 你好,我可能没表达清楚,其实你说的这些我还是都明白的,可能不明白的是 文件描述符的数据准备这个概念,这个什么叫做数据准备,读磁盘,还是什么,另外如果所有的文件描述符在一个线程里处理,文件准备这个操作也是在同一个线程里处理的,这样连接多了不会也会造成处理的很多连接处理的很慢吗,仅仅是为了省资源?
cc959798
2018-11-26 09:24:31 +08:00
@lhx2008 🤦‍♀️
gamexg
2018-11-26 10:10:40 +08:00
@cc959798 #6

https://segmentfault.com/a/1190000008836467

“ sk_data_ready: 通知 socket 数据包已经准备好”
cyspy
2018-11-26 10:40:17 +08:00
@cc959798 同步阻塞模式下高并发的时候就不只是慢的问题了, 比如 64bit Java 默认一个线程要 1M 内存,小机器很容易就吃满了
cc959798
2018-11-26 12:41:26 +08:00
@gamexg 你的意思是说,网卡或者说硬件设备数据到达内存或者处理的进程是比较慢的,相比建立连接来说,为了节约这部分时间,我们仅仅 handle 连接,不做处理,仅仅等待他数据到达了我们在处理,也就是说 io 多路复用节约的是这部分时间,对请求的处理该是多少就是多少?
gamexg
2018-11-26 13:05:26 +08:00
@cc959798 #10
io 多路复用不节省时间,不增加速度。

x 年前单机 10k 连接挑战时,如 @cyspy #9 所说,"比如 64bit Java 默认一个线程要 1M 内存,小机器很容易就吃满了",10k 个连接如果开 10k 个线程,光线程本身就会占用 1M*10k = 10G 内存,非常浪费了。

io 多路复用,复用的是线程。可以理解为使用同步阻塞模式,每个连接 read 都需要一个线程。而使用 io 多路复用意味着一个线程可以处理多个 socket 的 read。这里的意图是节省线程这个成本较高的资源。

然后注意 上个连接中 “调用完 sk_data_ready 之后,一个数据包处理完成,等待应用层程序来读取,上面所有函数的执行过程都在软中断的上下文中。”,也就是在 socket read 准备好之前的所有操作都是内核 ksoftirqd 进程负责的,并不需要用户进程的线程处理。既然之前的操作不需要用户线程,那么为什么要一个连接浪费一个线程等待 read 返回?所以慢慢发展出来了 epoll,一个线程可以等待一批 socket,反正在准备好之前都是内核 ksoftirqd 进程处理,用户线程一直在等待数据准备好,相当于等待一个锁。
cc959798
2018-11-26 14:20:08 +08:00
@gamexg 嗯嗯,谢谢,也就说分两个方面理解,不是加速而是能不能到问题,或者说节省了资源,加快了吞吐让人感觉是变快了,io 多路复用和阻塞着连接是一样的,只不过一个线程处理多个。
另外一个方面是读取网络来的请求时耗时的(不管具体怎样),我们没必要等着,发现有数据准备好的我们就开始读取数据

我一开始可能理解的有些问题,网上的资料都是着重于将 epoll 的原理,其他的不太详细,所以才有疑问😳

一开始想到的是,我们读取请求之后,处理请求都是一样的,无论你 io 多路复用还是阻塞着来,所以感觉总是时间是一样的,忽视了请求到来和读取完毕这个过程中还是有很多工作要做的,所以才疑惑,不过多谢啦
julyclyde
2018-11-26 15:59:40 +08:00
所谓快是相当于多进程多线程来说的
节省了 CPU 切换进程时的开销
chashao
2018-11-26 17:59:38 +08:00
我以为 IO 复用是 IO 的速度相对于太慢- -然后让 CPU 等待 IO 完成不值得,这样只能用多线程,但是多线程占用内存多,不过多线程可以让系统来调度,IO 复用就需要自己来维持连接状态- -不知道这样对不对

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/511406

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX