zoukankan      html  css  js  c++  java
  • io多路复用的讲解

    一、什么是socket?

    我们都知道unix(like)世界里,一切皆文件,而文件是什么呢?文件就是一串二进制流而已,不管socket,还是FIFO、管道、终端,对我们来说,一切都是文件,一切都是流。在信息 交换的过程中,我们都是对这些流进行数据的收发操作,简称为I/O操作(input and output),往流中读出数据,系统调用read,写入数据,系统调用write。不过话说回来了 ,计算机里有这么多的流,我怎么知道要操作哪个流呢?对,就是文件描述符,即通常所说的fd,一个fd就是一个整数,所以,对这个整数的操作,就是对这个文件(流)的操作。我们创建一个socket,通过系统调用会返回一个文件描述符,那么剩下对socket的操作就会转化为对这个描述符的操作。不能不说这又是一种分层和抽象的思想。

    二、阻塞?

    什么是程序的阻塞呢?想象这种情形,比如你等快递,但快递一直没来,你会怎么做?有两种方式:

    • 快递没来,我可以先去睡觉,然后快递来了给我打电话叫我去取就行了。
    • 快递没来,我就不停的给快递打电话说:擦,怎么还没来,给老子快点,直到快递来。

    很显然,你无法忍受第二种方式,不仅耽搁自己的时间,也会让快递很想打你。
    而在计算机世界,这两种情形就对应阻塞和非阻塞忙轮询。

      • 非阻塞忙轮询:数据没来,进程就不停的去检测数据,直到数据来。
      • 阻塞:数据没来,啥都不做,直到数据来了,才进行下一步的处理。

    先说说阻塞,因为一个线程只能处理一个套接字的I/O事件,如果想同时处理多个,可以利用非阻塞忙轮询的方式,伪代码如下:

    [cpp] view plain copy
     
     
     
     
    1. while true  
    2. {  
    3.     for i in stream[]  
    4.     {  
    5.         if i has data  
    6.         read until unavailable  
    7.     }  
    8. }  


    我们只要把所有流从头到尾查询一遍,就可以处理多个流了,但这样做很不好,因为如果所有的流都没有I/O事件,白白浪费CPU时间片。正如有一位科学家所说,计算机所有的问题都可以增加一个中间层来解决,同样,为了避免这里cpu的空转,我们不让这个线程亲自去检查流中是否有事件,而是引进了一个代理(一开始是select,后来是poll),这个代理很牛,它可以同时观察许多流的I/O事件,如果没有事件,代理就阻塞,线程就不会挨个挨个去轮询了,伪代码如下:

    [cpp] view plain copy
     
     
     
     
    1. while true  
    2. {  
    3.     select(streams[]) //这一步死在这里,知道有一个流有I/O事件时,才往下执行  
    4.     for i in streams[]  
    5.     {  
    6.         if i has data  
    7.         read until unavailable  
    8.     }  
    9. }  

    但是依然有个问题,我们从select那里仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

    epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))伪代码如下:

    [cpp] view plain copy
     
     
     
     
    1. while true  
    2. {  
    3.     active_stream[] = epoll_wait(epollfd)  
    4.     for i in active_stream[]  
    5.     {  
    6.         read or write till  
    7.     }  
    8. }  

    可以看到,select和epoll最大的区别就是:select只是告诉你一定数目的流有事件了,至于哪个流有事件,还得你一个一个地去轮询,而epoll会把发生的事件告诉你,通过发生的事件,就自然而然定位到哪个流了。不能不说epoll跟select相比,是质的飞跃,我觉得这也是一种牺牲空间,换取时间的思想,毕竟现在硬件越来越便宜了。

    三、I/O多路复用

    好了,我们讲了这么多,再来总结一下,到底什么是I/O多路复用。
    先讲一下I/O模型:
    首先,输入操作一般包含两个步骤:

    1. 等待数据准备好(waiting for data to be ready)。对于一个套接口上的操作,这一步骤关系到数据从网络到达,并将其复制到内核的某个缓冲区。
    2. 将数据从内核缓冲区复制到进程缓冲区(copying the data from the kernel to the process)。

    其次了解一下常用的3种I/O模型:

    io多路复用,是由对文件流的处理问题上而产生的,为了提高对流处理的能力。

    1.阻塞模式: 刚开始文件流处理的方式很粗暴,就是等待文件流处理,有可以读写的文件就执行,效率低。

    2,非阻塞忙轮询,就是无差别的扫描所有文件流,类似死循环查找文件流状态

    3.优化非阻塞模式,

    4.I/O复用模型,就是用到了select 和poll函数,select和poll函数可以同时阻塞多个IO操作。

    5信号驱动I/O模型(signal driven io,sigio)

    6.异步IO模型

    文章转自:https://blog.csdn.net/SkydivingWang/article/details/74917897

     
  • 相关阅读:
    398. Random Pick Index
    382. Linked List Random Node
    645. Set Mismatch
    174. Dungeon Game
    264. Ugly Number II
    115. Distinct Subsequences
    372. Super Pow
    LeetCode 242 有效的字母异位词
    LeetCode 78 子集
    LeetCode 404 左叶子之和
  • 原文地址:https://www.cnblogs.com/iifeng/p/11220722.html
Copyright © 2011-2022 走看看