IO模型概述BIO、NIO、多路复用、异步IO(select、poll、epoll)

IO模型种类:

  • 阻塞IO(bloking IO)
  • 非阻塞IO(non-blocking IO)
  • 多路复用IO(multiplexing IO)
  • 信号驱动式IO(signal-driven IO)
  • 异步IO(asynchronous IO)

IO角色-Linux环境:

  1. 系统内核(kernel)
  2. 程序(process or thread)

IO过程:

  1. 等待数据准备 (Waiting for the data to be ready)
  2. 将数据从内核拷贝到进程中(Copying the data from the kernel to the process)

阻塞IO(bloking IO)

IO模型概述BIO、NIO、多路复用、异步IO(select、poll、epoll)_第1张图片

当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。
    所以,blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。

非阻塞IO(non-blocking IO

IO模型概述BIO、NIO、多路复用、异步IO(select、poll、epoll)_第2张图片

当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。
    所以,在非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有。

多路复用IO(multiplexing IO)

IO模型概述BIO、NIO、多路复用、异步IO(select、poll、epoll)_第3张图片

  • select模型
  1. 单个进程监控的文件描述符有限,通常为1024*8个文件描述符。当然可以改进,由于select采用轮询方式扫描文件描述符。文件描述符数量越多,性能越差。
  2. 内核/用户数据拷贝频繁,操作复杂。select在调用之前,需要手动在应用程序里将要监控的文件描述符添加到fed_set集合中。然后加载到内核进行监控。用户为了检测时间是否发生,还需要在用户程序手动维护一个数组,存储监控文件描述符。当内核事件发生,在将fed_set集合中没有发生的文件描述符清空,然后拷贝到用户区,和数组中的文件描述符进行比对。再调用select也是如此。每次调用,都需要了来回拷贝。
  3. 轮回时间效率低,select返回的是整个数组的句柄。应用程序需要遍历整个数组才知道谁发生了变化。轮询代价大。
  4. select是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后select调用还是会将这些文件描述符返回,通知进程。
  • poll模型
  1. poll操作比select稍微简单点。select采用三个位图来表示fd_set,poll使用pollfd的指针,pollfd结构包含了要监视的event和发生的evevt,不再使用select传值的方法。更方便
  2. select的缺点依然存在.拿select为例,加入我们的服务器需要支持100万的并发连接。则在FD_SETSIZE为1024的情况下,我们需要开辟100个并发的进程才能实现并发连接。除了进程上下调度的时间消耗外。从内核到用户空间的无脑拷贝,数组轮询等,也是系统难以接受的。因此,基于select实现一个百万级别的并发访问是很难实现的。
  • epoll模型

epoll通过在linux申请一个建议的文件系统,把select调用分为了三部分。

  1. 调用epoll_create建立一个epoll对象,这个对象包含了一个红黑树和一个双向链表。并与底层建立回调机制。 
  2. 调用epoll_ctl向epoll对象中添加这100万个连接的套接字 
  3. 调用epoll_wait收集发生事件的连接。

从上面的调用方式就可以看到epoll比select/poll的优越之处:因为后者每次调用时都要传递你所要监控的所有socket给select/poll系统调用,这意味着需要将用户态的socket列表copy到内核态,如果以万计的句柄会导致每次都要copy几十几百KB的内存到内核态,非常低效。而我们调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。

异步IO(Asynchronous I/O)

IO模型概述BIO、NIO、多路复用、异步IO(select、poll、epoll)_第4张图片

用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

你可能感兴趣的:(网络)