V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
cc959798
V2EX  ›  问与答

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

  •  
  •   cc959798 · 2018-11-25 23:01:32 +08:00 · 3626 次点击
    这是一个创建于 2190 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    14 条回复    2018-11-26 17:59:38 +08:00
    ech0x
        1
    ech0x  
       2018-11-25 23:03:53 +08:00 via iPhone
    没有创建进程 /线程开销
    gamexg
        2
    gamexg  
       2018-11-25 23:36:47 +08:00   ❤️ 2
    特点不是快,而是可以节省系统资源。

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

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

    epoll 则是非阻塞的优化版本,不用自己去挨个尝试去读取所有 socket 看看哪个可用,系统在 socket 可用时通知应用层。
    lhx2008
        3
    lhx2008  
       2018-11-25 23:51:55 +08:00 via Android
    1.原来 IO 没返回要开线程等着,现在线程可以复用了
    2.应该是系统的 IO 缓冲区中,对方已经传了一些数据到内存了(不确定,看看楼下大神怎么说),线程可以来拿了
    3.应该在系统内核中的线程管理 ,具体原理我也不太清楚
    kran
        4
    kran  
       2018-11-26 00:55:15 +08:00 via Android
    不恰当类比一下,吞吐能力上去了,但单个响应不一定快的。
    secondwtq
        5
    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
        6
    cc959798  
    OP
       2018-11-26 09:23:25 +08:00
    @gamexg 你好,我可能没表达清楚,其实你说的这些我还是都明白的,可能不明白的是 文件描述符的数据准备这个概念,这个什么叫做数据准备,读磁盘,还是什么,另外如果所有的文件描述符在一个线程里处理,文件准备这个操作也是在同一个线程里处理的,这样连接多了不会也会造成处理的很多连接处理的很慢吗,仅仅是为了省资源?
    cc959798
        7
    cc959798  
    OP
       2018-11-26 09:24:31 +08:00
    @lhx2008 🤦‍♀️
    gamexg
        8
    gamexg  
       2018-11-26 10:10:40 +08:00
    @cc959798 #6

    https://segmentfault.com/a/1190000008836467

    “ sk_data_ready: 通知 socket 数据包已经准备好”
    cyspy
        9
    cyspy  
       2018-11-26 10:40:17 +08:00
    @cc959798 同步阻塞模式下高并发的时候就不只是慢的问题了, 比如 64bit Java 默认一个线程要 1M 内存,小机器很容易就吃满了
    cc959798
        10
    cc959798  
    OP
       2018-11-26 12:41:26 +08:00
    @gamexg 你的意思是说,网卡或者说硬件设备数据到达内存或者处理的进程是比较慢的,相比建立连接来说,为了节约这部分时间,我们仅仅 handle 连接,不做处理,仅仅等待他数据到达了我们在处理,也就是说 io 多路复用节约的是这部分时间,对请求的处理该是多少就是多少?
    gamexg
        11
    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
        12
    cc959798  
    OP
       2018-11-26 14:20:08 +08:00
    @gamexg 嗯嗯,谢谢,也就说分两个方面理解,不是加速而是能不能到问题,或者说节省了资源,加快了吞吐让人感觉是变快了,io 多路复用和阻塞着连接是一样的,只不过一个线程处理多个。
    另外一个方面是读取网络来的请求时耗时的(不管具体怎样),我们没必要等着,发现有数据准备好的我们就开始读取数据

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

    一开始想到的是,我们读取请求之后,处理请求都是一样的,无论你 io 多路复用还是阻塞着来,所以感觉总是时间是一样的,忽视了请求到来和读取完毕这个过程中还是有很多工作要做的,所以才疑惑,不过多谢啦
    julyclyde
        13
    julyclyde  
       2018-11-26 15:59:40 +08:00
    所谓快是相当于多进程多线程来说的
    节省了 CPU 切换进程时的开销
    chashao
        14
    chashao  
       2018-11-26 17:59:38 +08:00
    我以为 IO 复用是 IO 的速度相对于太慢- -然后让 CPU 等待 IO 完成不值得,这样只能用多线程,但是多线程占用内存多,不过多线程可以让系统来调度,IO 复用就需要自己来维持连接状态- -不知道这样对不对
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1628 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 16:55 · PVG 00:55 · LAX 08:55 · JFK 11:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.