你可以这么理解五种I/O模型

因为项目需要,接触和使用了Netty,Netty是高性能NIO通信框架,在业界拥有很好的口碑,但知其然不知其所以然。

所以本系列文章将从基础开始学起,深入细致的学习NIO。本文主要是介绍五种I/O模型,概念是枯燥的,不过还是得理解才行。

LINUX与UNIX中一些概念


在网络管理,Linux UNIX很相似.UNIX系统一直被用做高端应用或服务器系统,因此拥有一套完善的网络管理机制和规则, Linux沿用了这些出色的规则,使网络的可配置能力很强,为系统管理提供了极大的灵活性.

通俗一点讲,就是在网络方面Linux和UNIX是非常相似的,网络模型大可借鉴UNIX网络编程中的描述。

这里介绍四个概念,方便五种I/O模型的理解:

1.所有外部设备皆文件

Linux的内核将所有的外部设备都看作是一个文件来操作,对一个文件的读写操作会调用内核提供的系统命令,返回一个file descriptor(fd,文件描述符)。

面对一个socket也会有相应的描述符,成为socketfd(socket描述符),描述符就是一个数字,他指向内核中的一个结构体(文件路径,数据区等一些属性)。

2.recvfrom()函数

ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, strut sockaddr *from, socklen_t *addrlen);

该函数执行成功,则返回读或写的字节数,如出错则为-1。

*from参数,指向一个将由该函数在返回时填写数据包发送者的协议地址的套接字地址结构,

*addrlen参数,套接字地址结构,并且该结构体中填写的则放在addrlen所指的整数中返回给调用者

通过这两个参数,我们可以知道是谁发送了数据包(udp情况下),或是谁发送了数据包(TCP情况下);

3.应用进程与内核

 应用进程就是常规的程序,用户程序,打开任务管理器,在应用分组就可以看到应用进程,如下图所示:

图中红框内的,都是应用进程。所有的应用进程都是运行在用户态中。(用户态的概念直接戳链接),运行时所处空间是用户空间

内核就是操作系统的内核,它的作用是将应用进程与硬件分开。可以这么理解,所有涉及到I/O的操作都直接或者间接的经过内核程序。

如果应用进程可以直接操作硬件,那么一些病毒就会蓄意的对计算机硬件进行破坏,那就不可控制了。这样的机制就保证了系统的安全性。运行时是处于内核态,所处空间是内核空间。

网络传输数据,首先是内核先接收到数据,然后内核将数据拷贝到用户态中供应用进程使用。

请先理解上面的基本概念,接下来将介绍五种传统的I/O模型。

同步阻塞I/O


最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。

在应用进程通过内核调用recvfrom()函数,其系统调用直到数据包到达且被复制到应用进程的缓冲区或者发生错误时才会返回,在此期间会一直等待。

这句话太晦涩难懂了,简单点说就是:应用进程通过内核调用recvfrom(),收到数据的话则将数据从内核态复制到用户态,没有收到就一直阻塞。

同步非阻塞I/O


应用系统还是调用recvfrom,但是他不会阻塞与此,而是不断的去轮询的是否有数据准备好,如果没有准备好,就直接返回一个EWOULDBLOCK错误。

总结:

与同步阻塞I/O相比,如果数据准备好,不会一直阻塞与此,而是直接返回错误,接收到错误之后,就可以干点别的事,这是他的优点。但是缺点也很明显,

任务完成的响应延迟增大了。因为很可能在两次轮询之间,socketfd就处于read状态了,所以导致整体的吞吐量下降了。

 I/O复用


I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程

与同步非阻塞I/O不断轮询不同的是,I/O复用是使用一个线程循环轮询socketfd集合是否处于read状态。

Linux提供select/epoll,进程通过一个或者多个socketfd传递给select或poll系统调用,阻塞在select上,这样select/poll可以侦测到多个socketfd是否处于就绪状态。

select/poll是顺序扫描socketfd是否就绪,而且支持的fd很有限。

Linux还提供了一个epoll系统调用,epoll基于事件驱动方式代替顺序扫描,因此性能更高。当有fd就绪时,立即回调函数rollback。

关于select/poll,epoll

select/poll

该函数允许进程指示内核等待多个事件中的任何事件发生,并且只在有一个或多个事件发生或经历一段时间指定的时间才唤醒它。 举个例子,也就是说进程可以通知内核在socketfd集合{1,2,3}进行侦听,知道socketfd集合中任何一个可读的话,就返回。这个等待的过程是阻塞的,它可以侦听多个,但是侦听的数量是有限的。

看下官方关于epoll的解释

The epoll API performs a similar task to poll: monitoring multiple file descriptors to see if I/O is possible on any of them. 
The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.

epoll和poll执行类似的任务,监控多个fd,如果多个fd中任何一个有I/O时间,即可及时发现。epoll的API既可以作为边触发,也可以作为水平触发接口和可扩展到大量的监视fd。

epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量连接中只有少量活跃的情况下的系统CPU利用率.

epoll与select/poll的对比

一个进程能够打开socketfd的限制

select一个进程能够打开的FD是由FD_SETSIZE限制的,默认是2048,可以选择修改宏然后重新编译服务器代码,相关资料表明这样会带来网络效率的下降。

epoll没有打开FD数量的限制,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max查看,一般来说这个数目和系统内存关系很大。

IO效率不会因为socketfd数量提高而线性下降

select和poll拥有一个很大的socketfd集合和,由于每次都会调用线性扫描全部的集合,带来的后果就是效率线性下降

epoll有着相对更好的解决方案,在很大的socketfd集合中,它只会对活跃的socketfd进行操作。epoll是根据每个fd上面的callback函数实现的,只有活跃的socketfd才会去调用callback函数

mmap加速内核与用户空间的传递

无论是select/poll和epoll,都是需要通过内核将FD消息拷贝到用户空间,拷贝是费时的。epoll通过内核与用户控件mmap同一块内存实现不必要的拷贝,从而加快效率。

总结:

进程通过调用内核中的select/poll/epoll,监听socketfd集合的读写就绪状态,多个socketfd都能在一个线程中交替完成,所谓的复用就是指使用的同一个线程。

I/O复用实际还是同步I/O,归根到底还是应用进程主动向内核查询状态。

I/O多路复用是OS提供的最稳定的IO模型,大部分主流的应用都是基于此种IO模型构建的,比如NodeJS,Netty框架。

信号驱动I/O


首先开启套接字信号驱动I/O功能,并通过系统调用sigaction执行一个信号处理函数,此时系统继续运行,并不会阻塞。

当数据准备就绪时,就为该进程生成一个SIGIO信号,通过信号回调通知,通知应用进程调用recvfrom来读取数据。

异步非阻塞I/O


一句话简单说:产品经理让你改一个需求,并且让你改好了告诉他,给他看一下,于是你就吭哧吭哧的做了,(产品经理就去忙别的事情了,比如又去改需求了)并且做好了叫了产品经理来看。

用户进程进行aio_read系统调用之后,就去干别的事情了。当socketfd数据准备好之后,内核直接复制数据到用户空间,然后内核向用户进程发送通知,数据准备好了。

总结:

整个I/O过程都是非阻塞的,这个是真正的异步非阻塞。

 总结:


理解五种I/O模型,有助于理解网络I/O,写出更健壮的代码。

在实际工程项目中,普遍使用I/O复用模型,本章重点介绍了I/O复用模型,Java中的NIO也是基于此。学习I/O模型有助于更好的理解NIO,学习Netty框架。

勿在浮沙筑高楼

参考:

epoll百度百科

《UNIX网络编程》

《Netty权威指南》

聊聊五种I/O模型

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IT可乐

Linux系列教程(三)——Linux学习技巧

  前面我们讲了Linux系统的详细安装教程,大家跟着教程一步一步的操作,应该能完美的完成安装。那么这篇博客跟大家聊聊如何来学习Linux。 1、工欲善其事必先...

2297
来自专栏H2Cloud

C++ 后台程序实时性能监控

面对的问题: 做后台程序经常会被问一句话,你的程序能撑多少人。一般官方一点的回答是这个得根据实际情况而定。实际上后台程序的性能是可以被量化的。我们开发的每一个服...

3978
来自专栏鸿的学习笔记

流处理

流处理比起之前的批处理而言,需要考虑的东西更多。批处理有个前提,那就是输入必定是固定的大小,而流处理处理的数据是不会暂停的,与线上服务需要处理的数据也不一样,线...

921
来自专栏小白课代表

编程 | 计算机等级考试——VC++2010 Express学习版

2862
来自专栏北京马哥教育

Python 中的进程、线程、协程、同步、异步、回调

进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生? 在刚刚结束的 PyCon2014 上海站,来自七...

4305
来自专栏一枝花算不算浪漫

【python】Python 资源大全中文版

3972
来自专栏CSDN技术头条

如何提升Java应用程序性能

【摘要】本文首先介绍了负载测试、基于APM工具的应用程序和服务器监控,随后介绍了编写高性能Java代码的一些最佳实践。最后研究了JVM特定的调优技巧、数据库端的...

2277
来自专栏熊二哥

快速入门系列--WCF--08扩展与新特性

最后一章将进行WCF扩展和新特性的学习,这部分内容有一定深度,有一个基本的了解即可,当需要自定义一个完整的SOA框架时,可以再进行细致的学习和实践。 ? 服务...

2127
来自专栏腾讯移动品质中心TMQ的专栏

Android内存泄露测试不再蓝瘦,香菇

在进行Android内存泄露分析时,面对成千上万个对象,你是否蓝瘦,香菇?作为测试人员你在进行内存泄露测试之后,是否有勇气告诉开发同事程序已经没有内存泄露,可以...

2777
来自专栏大史住在大前端

webpack4.0各个击破(6)—— Loader篇

loader是webpack的核心概念之一,它的基本工作流是将一个文件以字符串的形式读入,对其进行语法分析及转换(或者直接在loader中引入现成的编译工具,例...

1281

扫码关注云+社区

领取腾讯云代金券