浅析Linux中的五种IO模型(下)

简介: IO是什么I/O(Input/Output),中文名为输入/输出,指的是一切操作程序或设备与计算机之间发生的数据传输的过程。它分为IO设备和IO接口两个部分。

特点

上面的图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。(select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)

在IO 多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的进程其实是一直被block的。只不过进程是被select这个函数block,而不是被socket IO给block。所以IO多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。

在I/O编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理。I/O多路复用技术通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降底了系统的维护工作量,节省了系统资源。

虽然多路复用产生的效果,完全可以由用户态去遍历文件描述符并调用其非阻塞的 read 函数实现。但是多路复用快的原因在于,操作系统提供了这样的系统调用,使得原来的 while 循环里多次系统调用,变成了一次系统调用 + 内核层遍历这些文件描述符。

I/O多路复用的主要应用场景如下:

  1. 服务器需要同时处理多个处于监听状态或者多个连接状态的套接字。
  2. 服务器需要同时处理多种网络协议的套接字。

阻塞IO、非阻塞IO、IO多路复用的异同

在用户进程进行系统调用的时候,他们在等待数据到来的时候,处理的方式不一样。有直接等待,轮询,select、poll或epoll轮询这三种方式。

IO交互两个阶段过程:

  • 第一个阶段有的阻塞,有的不阻塞,有的可以阻塞又可以不阻塞。
  • 第二个阶段都是阻塞的。

从整个IO过程来看,他们都是顺序执行的,因此可以归为同步模型(synchronous),都是进程主动等待且向内核检查状态。

同时,高并发的程序一般使用同步非阻塞方式而非多线程 + 同步阻塞方式。

信号驱动IO模型(signal blocking I/O)

使用信号,让内核在文件描述符就绪的时候使用 SIGIO 信号来通知我们。我们将这种模式称为信号驱动 I/O 模式。

网络异常,图片无法展示
|


允许Socket使用信号驱动 I/O ,还要注册一个 SIGIO 的处理函数,这时的系统调用将会立即返回。然后我们的程序可以继续做其他的事情,当数据就绪时,进程收到系统发送一个 SIGIO 信号,可以在信号处理函数中调用IO操作函数处理数据。

信号驱动IO在实际中并不常用。


异步IO模型(asynchronous I/O)


场景描述

女友即不想逛街,又觉得餐厅太吵了,因此,决定回家好好休息一下。于是我们叫外卖,打个电话点餐,然后我和女友可以在家好好休息一下了,当饭好了,送货员送到家里来。这就是典型的异步,只需要打个电话说一下,然后可以做自己的事情,饭好了就送来。

网络模型

相对于同步IO,异步IO不是顺序执行。用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO的两个阶段,进程都是非阻塞的。

Linux提供了AIO库函数实现异步,但是用的很少。目前有很多开源的异步IO库,例如libevent、libev、libuv。

网络异常,图片无法展示
|


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

在 Linux 中,通知的方式是 “信号”:

  • 如果这个进程正在用户态忙着做别的事(例如在计算两个矩阵的乘积),那就强行打断,调用事先注册的信号处理函数,这个函数可以决定何时以及如何处理这个异步任务。由于信号处理函数是突然闯进来的,因此跟中断处理程序一样,有很多事情是不能做的,因此保险起见,一般是把事件 “登记” 一下放进队列,然后返回该进程原来在做的事。
  • 如果这个进程正在内核态忙着做别的事,例如以同步阻塞方式读写磁盘,那就只好把这个通知挂起来了,等到内核态的事情忙完了,快要回到用户态的时候,再触发信号通知。
  • 如果这个进程现在被挂起了,例如,无事可做 sleep 了,那就把这个进程唤醒,下次有 CPU 空闲的时候,就会调度到这个进程,触发信号通知。

Linux下的异步IO其实用得很少 ,著名的高性能网络框架netty 5.0版本被废弃的原因便是:使用异步IO提升效率,增加了复杂性,却并且没有显示出明显的性能优势。

异步IO的两个阶段:

第一阶段:当在异步 I/O 模型下时,用户进程如果想进行 I/O 操作,只需进行系统调用,告知内核要进行 I/O 操作,此时内核会马上返回, 用户进程 就可以去处理其他的逻辑了 。

第二阶段:当内核完成所有的 I/O 操作和数据拷贝后,内核将通知我们的程序,此时数据已经在用户空间了,可以对数据进行处理了。

非阻塞IO和异步IO的区别

在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。

而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

总结


网络异常,图片无法展示
|


前四种I/O模型都是同步I/O操作,他们的区别在于第一阶段,而他们的第二阶段是一样的。在数据从内核复制到应用缓冲区期间(用户空间),进程阻塞于系统调用。

相反,异步I/O模型在这等待数据和接收数据的这两个阶段里面都是非阻塞的,可以处理其他的逻辑,用户进程将整个IO操作交由内核完成,内核完成后会发送通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

相关文章
|
6月前
|
Java Linux API
IO模型
BIO、NIO、AIO是Java中处理网络I/O的三种模型。BIO为阻塞式,每个连接需单独线程,高并发下性能受限;NIO通过非阻塞与多路复用提升并发能力,少量线程可处理大量请求;AIO进一步实现异步非阻塞,数据复制时线程可释放,由回调机制处理后续操作。三者适用于不同场景,BIO易用但低效,NIO高效但复杂,AIO理论性能更优但目前在Linux上仍依赖多路复用实现。Java 21引入虚拟线程后,BIO也可兼具高性能与易编写特性。
194 2
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
700 1
Linux C/C++之IO多路复用(aio)
|
7月前
|
Linux C语言 网络架构
Linux的基础IO内容补充-FILE
而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲,此时我们使用printf和fwrite函数打印的数据都打印到了C语言自带的缓冲区当中,之后当我们使用fork函数创建子进程时,由于进程间具有独立性,而之后当父进程或是子进程对要刷新缓冲区内容时,本质就是对父子进程共享的数据进行了修改,此时就需要对数据进行写时拷贝,至此缓冲区当中的数据就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和fwrite函数打印的数据就有两份。此时我们就可以知道,
118 0
|
7月前
|
存储 Linux Shell
Linux的基础IO
那么,这里我们温习一下操作系统的概念我们在Linux平台下运行C代码时,C库函数就是对Linux系统调用接口进行的封装,在Windows平台下运行C代码时,C库函数就是对Windows系统调用接口进行的封装,这样做使得语言有了跨平台性,也方便进行二次开发。这就是因为在根本上操作系统确实像银行一样,并不完全信任用户程序,因为直接开放底层资源(如内存、磁盘、硬件访问权限)给用户程序会带来巨大的风险。所以就向银行一样他的服务是由工作人员隔着一层玻璃,然后对顾客进行服务的。
104 0
|
11月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
489 34
|
Linux API C语言
Linux基础IO
Linux基础IO操作是系统管理和开发的基本技能。通过掌握文件描述符、重定向与管道、性能分析工具、文件系统操作以及网络IO命令等内容,可以更高效地进行系统操作和脚本编写。希望本文提供的知识和示例能帮助读者更深入地理解和运用Linux IO操作。
255 14
|
网络协议 前端开发 Java
网络协议与IO模型
网络协议与IO模型
494 4
网络协议与IO模型
|
开发者
什么是面向网络的IO模型?
【10月更文挑战第6天】什么是面向网络的IO模型?
130 3
|
数据挖掘 开发者
网络IO模型
【10月更文挑战第6天】网络IO模型
504 3