linux io模型 linux中断向量表

干货 五种IO模型的特点以及比较

为了保证操作系统的安全,将内存划分为内核空间和用户空间。内核空间的进程,可以访问硬件执行IO等操作,用户空间的进程只能通过系统调用来访问IO等系统资源。

对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后再拷贝到应用程序的地址空间。

所以,当一个read操作发生时,它会经历两个阶段

正是因为这两个阶段,linux系统产生了下面五种网络模型:

在linux下,默认情况下所有的socket都是blocking的,流程如下图所示:

进程调用recvfrom系统调用来读取数据,这是如果还没有到达,进程就进入阻塞状态。等数据到达后完成到内核去的拷贝,再从内核拷贝到用户空间,用户进程才解除阻塞状态。

特点:在IO执行的两个阶段进程都会都阻塞

执行非阻塞io系统调用时,如果内核中的数据还没有准备好,会直接返回,不会阻塞。通过进程不断查询,直到数据在内核中就绪,便开始拷贝到用户空间。拷贝的过程中,进程还是被阻塞了,所有非阻塞IO也是同步IO。

特点:需要进程不断地主动询问kernel数据是否准备好了

单个进程处理多个网络连接IO,使用select\poll\epoll三种系统调用,不断轮询所有的连接,如果有数据到达内核则通知进程,进行数据拷贝到用户内存。

当调用select时,进程会进入阻塞状态,直到有数据到达。

这个图看起来和阻塞IO区别不大,甚至还多使用了一个系统调用

但它的优势在于可以同时监控多个IO连接。

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

linux下的异步io使用的很少,其流程如下:

发起read操作后进程立马返回,整个Io过程不会产生任何block。kernel会等等数据准备完成,然后将数据拷贝到用户内存。当这一切都完成后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

调用blocking io会一直block进程直到操作完成

no-blocking io在kernel准备数据的阶段是会立刻返回的

只有异步IO是异步IO,

其他3种:阻塞IO、非阻塞IO、多路复用IO都是同步的。

这是因为其他三种IO在执行真实IO操作的过程中都有进程阻塞的阶段,而异步IO在整个过程中进程都没有被阻塞。非阻塞IO在内核数据就绪,拷贝到用户空间的阶段也是阻塞的,因此也是同步IO。

Linux IO模式及select、poll、epoll详解

Linux磁盘IO、网络IO、零拷贝详解

在计算机操作系统的领域,I/O(输入/输出)指的是数据从计算机系统外部到内部,或者从内部到外部的传输过程,通常可以分为磁盘I/O和网络I/O两类。

磁盘I/O模型和网络I/O模型的底层操作本质都是用户空间和内核空间之间的数据传输。在Linux操作系统中,由于安全性的考虑,用户进程无法直接访问内核空间的数据,因此数据传输需要经过从用户空间到内核空间,再到物理设备,最后返回用户空间的路径。

磁盘I/O的读操作过程中,当应用程序发起read()请求时,系统首先检查内核缓冲区是否已存有所需数据,若存在直接复制至用户空间;若不存在,则通过DMA方式从磁盘读取数据至缓冲区,再复制至用户空间。写操作则需先将数据从用户空间复制至内核缓冲区,具体步骤涉及三次拷贝:磁盘读取、用户空间至内核空间、内核空间至磁盘。

网络I/O的读写操作与磁盘I/O类似,读操作中数据可能来源于物理磁盘或socket,写操作中假设数据来源于磁盘,流程与磁盘I/O的写操作相似。网络I/O的延迟主要由服务器响应时间、带宽限制、网络延迟、路由跳转延迟和接收延迟等综合决定,通常大于磁盘I/O。

在磁盘I/O中,使用缓冲机制来提高效率和保护磁盘,但存在数据在应用程序地址空间和页缓存之间多次拷贝的操作,引入了开销。为解决这一问题,直接I/O(Direct I/O)方式被引入,数据直接在用户地址空间的缓冲区和磁盘之间传输,减少了不必要的拷贝。

mmap(内存映射)是另一种内存管理机制,它将页缓存地址空间映射到用户空间,允许用户进程直接对页缓存进行读写操作,实现了一种特殊的缓存I/O。

Linux中的五大网络I/O模型包括BIO(阻塞模式IO)、NIO(非阻塞模式IO)、I/O多路复用模型、信号驱动IO和异步IO(AIO)。这些模型分别采用不同的方式处理I/O请求,以适应不同的应用场景和性能需求。

总结而言,I/O操作是计算机系统中至关重要的环节,涉及数据在不同空间的高效传输。Linux操作系统通过各种机制优化I/O性能,满足不同应用的需要。

Linux异步IO

Linux中最常用的IO模型是同步IO,在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足条件为止。这是一种很好的解决方案,调用应用程序在等待IO完成的时候不需要占用CPU,但是在很多场景中,IO请求可能需要和CPU消耗交叠,以充分利用CPU和IO提高吞吐率。

下图描绘了异步IO的时序,应用程序发起IO操作后,直接开始执行,并不等待IO结束,它要么过一段时间来查询之前的IO请求完成情况,要么IO请求完成了会自动被调用与IO完成绑定的回调函数。

Linux的AIO有多种实现,其中一种实现是在用户空间的glibc库中实现的,本质上是借用了多线程模型,用开启的新的线程以同步的方式做IO,新的AIO辅助线程与发起AIO的线程以pthread_cond_signal()的形式进行线程间的同步,glibc的AIO主要包含以下函数:

1、aio_read()

aio_read()函数请求对一个有效的文件描述符进行异步读操作。这个文件描述符可以代表一个文件、套接字,甚至管道,aio_read()函数原型如下:

aio_read()函数在请求进行排队之后就会立即返回(尽管读操作并未完成),如果执行成功就返回0,如果出现错误就返回-1。参数aiocb(AIO I/O Control Block)结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓冲区。在产生IO完成通知时,aiocb结构就被用来唯一标识所完成的IO操作。

2.aio_write()

aio_write()函数用来请求一个异步写操作。函数原型如下:

aio_write()函数会立即返回,并且它的请求以及被排队(成功时返回值为0,失败时返回值为-1)

3.aio_error()

aio_error()函数被用来确定请求的状态,其原型如下:

该函数的返回:

4.aio_return()

异步IO和同步阻塞IO方式之间有一个区别就是不能立即访问函数的返回状态,因为异步IO没有阻塞在read()调用上。在标准的同步阻塞read()调用中,返回状态是在该函数返回时提供的。

但是在异步IO中,我们要用aio_return()函数,原型如下:

只有在aio_error()调用确定请求已经完成(可能成功、也可能发生了错误)之后,才会调用这个函数,aio_return()的返回值就等价于同步情况中read()或者write系统调用的返回值。

5.aio_suspend()

用户可以用该函数阻塞调用进程,直到异步请求完成为止,调用者提供了一个aiocb引用列表,其中任何一个完成都会导致aio_suspend()返回。函数原型如下:

6.aio_cancel()

该函数允许用户取消对某个文件描述符执行的一个或所以IO请求。

要取消一个请求,用户需要提供文件描述符和aiocb指针,如果这个请求被成功取消了,那么这个函数就会返回AIO_CANCELED。如果请求完成了,就会返回AIO_NOTCANCELED。

7.lio_listio()

lio_listio()函数可用于同时发起多个传输。这个函数非常重要,它使得用户可以在一个系统调用中启动大量的IO操作,原型如下:

mode参数可以是LIO_WAIT或者是LIO_NOWAIT。LIO_WAIT会阻塞这个调用,直到所有的IO都返回为止,若是LIO_NOWAIT模型,在IO操作完成排队之后,该函数就会返回。list是一个aiocb的列表,最大元素的个数是由nent定义的。如果list的元素为null,lio_listio()会将其忽略。

阅读剩余
THE END