五种IO模型梳理

 

什么是IO?

无论是Socket的读写还是文件的读写,在Java层面的应用开发或者是linux系统底层开发,都属于输入input和输出output的处理,简称为IO读写。在原理上和处理流程上,都是一致的。区别在于参数的不同。用户程序进行IO的读写,基本上会用到read&write两大系统调用。

read:是把数据从内核缓冲区复制到进程缓冲区(内核空间到用户空间)。

write:是把数据从进程缓冲区复制到内核缓冲区(用户空间到内核空间)。

这个两个系统调用,都不负责数据在内核缓冲区和磁盘之间的交换。底层的读写交换,是由操作系统kernel内核完成的。

5中IO模型:

  • 阻塞I/O(blocking IO)
  • 非阻塞I/O(noblocking IO)
  • I/O复用    (IO multiplexing )
  • 信号驱动I/O (signal driven IO)
  • 异步I/O (asynchronous IO)

缓存I/O

  缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。用户空间没法直接访问内核空间的。

缺点: 数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

文件描述符fd

   文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。 
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时, 内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

阻塞I/O

使用recv的默认参数,但由于内核还未准备好数据报,应用进程就会阻塞住,一直等数据直到拷贝到用户空间,这段时间内进程始终阻塞。打个比方,A同学排队买票,他只能排队买上票才可以离开。这一过程就可以看成使用了阻塞       IO模型,因为如果在没买到票之前,他不能离开队伍做别的事情(离开等于白排队,回来又要重新排队)。很显然这种,I/O模型是同步的。

非阻塞I/O

 改变flags,让recv不管有没有获取到数据都返回,然后通过轮询的方式调用recv不停的去和内核交互,直到内核准备好数据,在两次发送请求的时间段,进程可以先做别的事情。对比阻塞模型,相当于A同学买票过程中,采用了取号买票,再没有到他前,他可以不断的返回购票大厅看下是不是到了自己的号,中间的过程可以做其他事情。他就不用向之前一样一刻不能离开购票大厅。这就是非阻塞IO模型。但是它只有是检查无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(到自己的号买上票),因此它还是同步IO。

信号驱动I/O

用户进程通过调用sigaction注册信号函数,然后返回,并且不阻塞,当内核数据准备就绪时会发送一个信号给进程,用户进程便在信号处理函数中开始把数据拷贝的用户空间中。A同学让售票服务人员等有票的时候通知他(注册信号函数),    没多久A同学得知有票了,跑去买票。这种还是同步IO(省不了买票的时间啊)。

I/O复用

 IO多路转接是多了一个select/epoll函数,这里在调用recv前先调用select或者epoll,多个进程的IO可以注册到同一个select上,当用户进程调用该select,select会监听所有注册好的IO,如果所有被监听的IO需要的数据都没有准备好时,select调用进程会阻塞。当任意一个IO所需的数据准备好之后,select调用就会返回,然后进程在通过recvfrom来进行数据拷贝。进程在发出select后,要等到select监听的所有IO操作中至少有一个需要的数据准备好,才会有返回,并且也需要再次发送请求去进行文件的拷贝。

    这种IO模型比较特别。因为它能同时监听多个文件描述符(fd)。举例A同学来北京到南京的车票,发现有一排售票窗口,售票服务人员告诉他这些窗口目前没有票,等有票告诉他。于是等啊等(select调用中),过了一会售票服务人员告诉他有票了,但不知道是哪个窗口卖北京到南京的车票,自己看吧。于是A同学一个个窗口问,直到找到卖北京到南京车票的窗口买上票(recv)。这里再顺便说说鼎鼎大名的epoll(高性能的代名词啊),epoll也属于IO复用模型,主要区别在于售票服务人员告诉他A同学哪几个窗口卖北京到南京的车票,不需要一个个去问了。

多路复用IO的特点:

IO多路复用模型,建立在操作系统kernel内核能够提供的多路分离系统调用select/epoll基础之上的。多路复用IO需要用到两个系统调用(system call), 一个select/epoll查询调用,一个是IO的读取调用。

和NIO模型相似,多路复用IO需要轮询。负责select/epoll查询调用的线程,需要不断的进行select/epoll轮询,查找出可以进行IO操作的连接。另外,多路复用IO模型与前面的NIO模型,是有关系的。对于每一个可以查询的socket,一般都设置成为non-blocking模型。只是这一点,对于用户程序是透明的(不感知)。

优点:

用select/epoll的优势在于,它可以同时处理成千上万个连接(connection)。与一条线程维护一个连接相比,IO多路复用技术的最大优势是:系统不必创建线程,也不必维护这些线程,从而大大减小了系统的开销。Java的NIO(new IO)技术,使用的就是IO多路复用模型。在linux系统上,使用的是epoll系统调用。

缺点:

本质上,select/epoll系统调用,属于同步IO,也是阻塞IO。都需要在读写事件就绪后,自己负责进行读写,也就是说这个读写过程是阻塞的。

异步I/O

用户进程调用aio_read,给内核传递描述符、缓冲区指针、缓冲区大小等,告诉内核当整个操作完成时,如何通知进程,然后就立刻去做其他事情了。内核收到请求,数据准备好以后,直接把数据拷贝到用户空间,然后再通知进程本次IO已经完成。A同学让售票服务人员帮他买好票后通知他。整个过程A同学都可以做别的事情(没有           recv),这才是真正的异步IO。

 

总结

IO分两阶段:

  1. 数据准备阶段

  2. 内核空间复制回用户进程缓冲区阶段

阻塞IO模型、非阻塞IO模型、IO复用模型(select/poll/epoll)、信号驱动IO模型都属于同步IO,因为阶段2是阻塞的(尽管时间很短)。只有异步IO模型是符合异步IO操作含义的,不管在阶段1还是阶段2都可以干别的事。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值