【IO】IO模型

首先需要区分几个概念

非阻塞I/O,字符转换,缓冲以及通道
从JDK 7版本开始,Java新加入的文件和网络io特性称为nio2(new io 2, 因为jdk1.4中已经有过一个nio了),包含了众多性能和功能上的改进,其中最重要的部分,就是对异步io的支持,称为Java AIO(asynchronous IO)。
因为AIO的实施需充分调用OS参与,IO需要操作系统支持、并发也同样需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。所以本文也附带介绍了Linux 2.6及以后版本新增的AIO特性(因为这跟Java AIO是对应关系)

  1. IO分为内存IO/网络IO/磁盘IO,磁盘IO都是阻塞的
  2. 阻塞与非阻塞是通过代码来实现的,区别在于是在于发过来操作请求,数据准备好才返回(阻塞)还是直接返回(非阻塞)
  3. IO读取顺序:磁盘(磁盘IO)/网卡(网络IO)—> 内核缓冲区 —> 用户内存,重点在于后面的过程是否是需要进程阻塞等待的

IO模型

  1. 同步阻塞(blocking IO)
    A去钓鱼了,你一直在那等,鱼上钩了,然后把鱼钓上来(全程阻塞)

    普遍使用的IO模型,linux默认的IO模型。
    进程调用recvfrom一直到recvfrom返回

  2. 同步非阻塞(noblocking IO)
    B去钓鱼了,放好钩后,然后开始看书,刷抖音去了,过一会看看是不是鱼上钩了。然后等鱼上钩了,把鱼钓上来
    这个询问其实询问的是操作系统内核,即文件描述缓冲区是否就绪,准备好了,就进行拷贝数据包的操作。没有数据报准备好时,也不阻塞程序,内核直接返回为准备就绪的信号。
    但是这个轮询其实是对CPU来说是一个比较大的消耗

    很少使用,因为他浪费了大量的CPU资源

    进程recvfrom,如果没有准备就绪的话,直接返回EWOULDBLOCK,过段时间再次调用recvfrom,直到正常返回。这个操作其实就是epolling(轮询),epolling内核是一个很占用CPU资源的操作

    但是对管道的操作,最好使用非阻塞的方式

    • nginx和node对于本地文件的IO使用的是,以线程的方法模拟非阻塞的效果
    • 对于静态文件的IO使用的是zero copy(例如sendfile)的效率是非常高的
  3. 信号驱动IO(signal blocking IO)

    C去钓鱼了,放好钩,但是这个时候他在鱼竿上放了一个小铃铛,然后去看书刷抖音了,等鱼上钩了,铃铛就响了,她就可以把鱼钓上来了
    进程告诉内核,数据报准备好的时候,你告诉我一个信号,我对sinal信号进行捕捉,然后调用信号处理函数来

    他要求套接字一定允许使用异步IO,设置简单,但是困难的是判定SIGIO信号产生的时候程序处于什么状态,UDP的话也就是接收到数据报,或者发生异步错误,但是TCP的情况就太多了。
    NTP服务器,它使用UDP,

  4. IO多路复用(IO multiplexing)

    D也去钓鱼了,但是D带了很多鱼竿,D用的方法和B差不多,他来回走着看是否有鱼上钩了,上钩了就收杆

    这样增加了效率,减少了等待的时间,IO多路转接多了一个select函数,其中一个参数是文件描述符的集合,他循环对这些文件描述符进行监听,当某个文件描述符状态改为就绪,就处理这个,select只负责等,recvfrom只负责拷贝。

    IO多路复用用到了select()和poll()或者epoll()(2.6开始)函数,主要在这些函数上面阻塞了,调用select的时候,只有准备就绪了才会返回,在单个IO操作下,和同步阻塞是没有任何区别的,高级之处在于对于多个IO的监听,所以他用在多个IO/多类型/多协议的场景下

  5. 异步IO (asynchronous IO)
    E也想钓鱼,但是E有事情,他让F来帮他钓鱼,F掉到鱼了,就通知E,E来收杆。
    应用程序调用aio_read,内核一方面去取数据报内容返回,另一方面将控制权还给应用程序。应用程序继续处理其他事情,是一种非阻塞的。
    之前的都只是不管怎么说,最多也就是等待过程非阻塞,现在读都是非阻塞的,这才是异步。

IO的过程其实就两部,一部分等待读或者写就绪(也就是等),一部分就是读或者(数据搬迁)
以上五种模型的效率。A<B<D<C<E

select poll epoll区别

F同样去钓鱼,但是他想钓的鱼是金枪鱼,他告诉G帮他看着,接下来看三种方式下,G的区别

select:G一直去看有鱼上钩了没有,有鱼上钩了,他就去看看这个鱼是不是金枪鱼,是的话他就通知F,但是G最多只能管理1024个杆。无差别轮询,处理的流越多,轮询时间越长,时间复杂度O(n)

poll:poll的方式和select一样,但是不限制数量,它是用链表存储的。时间复杂度O(n)

epoll:epoll可以理解为event poll,epoll的做法相当于给每条鱼都打上了标签,这样鱼上钩了,自动就知道什么鱼了。epoll基于内核的反射机制。在有活跃的socket的时候,系统会调用我们提前设置好的回掉函数。这样就比select/poll轮询方便很多

以上最重要的区别就是epoll把轮询的操作托让给了内核去做,因为内核更高效。但是托让给内核,我们调用了系统调用,如果轮询结果为空,也没有wakeup或新消息处理,这样就会发生空轮询,CPU使用率就会100%

  • selector.select()操作是阻塞的,只有被监听的fd有读写操作时,才被唤醒

netty对于此问题解决方案是对Selector的select操作周期进行统计,没完成一次空的select操作就进行一次计数。当在某个周期内发生了N次空轮询,就触发epoll死循环BUG。然后重建Selector,判断是否是其他线程发起的重建请求,若不是讲原来的SocketChannel从旧的Selector上去除注册。注册到新的Selector,并把之前的给关闭

windows 上面的iocp模型是aio模型,他会在哪只操作完IO以后,通过get函数获取一个完成事件的通知。

NIO场景

NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。

如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。一个线程多个连接的设计方案如下图所示:

如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

C10K/M问题

OS内核中的两个基本问题:

连接数=线程数/进程数:当一个数据包进来,内核会遍历其所有进程以决定由哪个进程来处理这个数据包。
连接数=选择数/轮询次数(单线程):同样的可扩展性问题,每个包都要走一遭列表上所有的socket。

通过上述针对Apache所表现出的问题,实际上彻底解决并发性能问题的解决方法的根本就是改进OS内核使其在常数时间内查找,使线程切换时间与线程数量无关,使用一个新的可扩展epoll()/IOCompletionPort常数时间去做socket查询。

因为线程调度并没有得到扩展,所以服务器大规模对socket使用epoll方法,这样就导致需要使用异步编程模式,而这些编程模式正是Nginx和Node类型服务器具有的。所以当从Apache迁移到Nginx和Node类型服务器时,即使在一个配置较低的服务器上增加连接数,性能也不会突降。所以在处理C10K连接时,一台笔记本电脑的速度甚至超过了16核的服务器。这也是前一个10年解决C10K问题的普遍方法。

1千万的并发连接数;
100万个连接/秒:每个连接以这个速率持续约10秒;
10GB/秒的连接:快速连接到互联网;
1千万个数据包/秒:据估计目前的服务器每秒处理50K数据包,以后会更多;
10微秒的延迟:可扩展服务器也许可以处理这个规模(但延迟可能会飙升);
10微秒的抖动:限制最大延迟;
并发10核技术:软件应支持更多核的服务器(通常情况下,软件能轻松扩展到四核,服务器可以扩展到更多核,因此需要重写软件,以支持更多核的服务器)

硬件不是C10M 问题的性能瓶颈所在处,真正的问题出在软件上,内核不做重的业务逻辑

参考

https://blog.csdn.net/ccj2020/article/details/7739880

https://blog.csdn.net/ZWE7616175/article/details/80591587

https://www.cnblogs.com/wt645631686/p/8528912.html

https://www.cnblogs.com/aspirant/p/9166944.html

posted @ 2019-08-28 10:26  colin_xun  阅读(214)  评论(0编辑  收藏  举报