Nginx 高效核心 - IO 复用模型

1. 图示 Nginx 服务处理请求简略过程

2. I/O 模型概述

2.1 同步/异步

      同步:Synchronous,调用者等待被调用者返回消息,才能继续执行。

异步:Asynchronous,被调用者通过状态、通知或回调机制主动通知调用者自己的运行状态。

2.2 阻塞/非阻塞

      阻塞:Blocking,指 IO 操作需要彻底完成之后才返回到用户空间,调用结果返回之前,调用者被挂起。

非阻塞:Noblocking,指 IO 操作被调用后立即返回给用户一个状态值,无需等到 IO 操作彻底完成,最终的调用结果返回之前,调用者不会被挂起。

2.3 常用的 I/O 模型

      阻塞型、非阻塞型、复用型、信号驱动型、异步。

3. 五种常用 I/O 模型

3.1 同步阻塞 I/O 模型

3.1.1 同步阻塞 I/O 模型图示

3.1.2 同步阻塞 I/O 模型说明

① 同步阻塞 I/O 模型是最简单的 I/O 模型,用户线程在内核进行 I/O 操作时被阻塞。

②用户线程通过系统调用 read 发起 I/O 读操作,由用户空间转到内核空间。内核等到数据包到达后,将接收的数据拷贝到用户空间,完成 read 操作。

③ 用户需要等待 read 将数据读取到 Buffer 后,才继续处理接收的数据,整个 I/O 请求的过程中,用户线程是被阻塞的,这导致用户在发起 IO 请求时,不能做任何事情,对 CPU 的资源利用率不高。

3.2 同步非阻塞 I/O 模型

3.2.1 同步非阻塞 I/O 模型图示

3.2.2 同步非阻塞 I/O 模型说明

① 用户线程发起 IO 请求时立即返回,但并未读取到任何数据,用户线程需要不断地发起 IO 请求,直到数据到达后,才真正读取到数据,继续执行,即 “轮询” 机制。

② 整个 IO 请求的过程中,虽然用户线程每次发起 IO 请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的 CPU 资源。

③ 是比较浪费 CPU 的方式,一般很少直接使用这种模型,而是在其他 IO 模型中使用非阻塞这一特性。

3.3 IO 多路复用模型

3.3.1 IO 多路复用模型图示

3.3.2 IO 多路复用模型说明

① 多个连接共用一个等待机制,本模型会阻塞进程,但是进程是阻塞在 select 或者 poll 这两个系统调用上,而不是阻塞在真正的 IO 操作上。

② 用户首先将需要进行 IO 操作添加到 select 中,继续执行其他的工作(异步),同时等待 select 系统调用返回。当数据到达时,IO 被激活,select 函数返回。用户线程正式发起 read 请求,读取数据并继续执行。

③ 从流程上来看,使用 select 函数进行 IO 请求和同步阻塞模型没有太大的区别,甚至还多了添加监控 IO,以及调用 select 函数的额外操作,效率更差,并且阻塞了两次,但是第一次阻塞在 select 上时,select 可以监控多个 IO 上是否已经有 IO 操作准备就绪,即可达到在同一个线程内同时处理多个  IO 请求的目的,而不像阻塞 IO 一次只能监控一个 IO 。

④ 虽然上述方式允许单线程内处理多个 IO 请求,但是每个 IO 请求的过程还是阻塞的(在 select 函数上阻塞),平均时间甚至比同步阻塞 IO 模型还要长,如果用户线程只是注册自己需要的 IO 请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高 CPU 的利用率。

⑤ IO 多路复用是最常用的 IO 模型,但是其异步程度还不够彻底,因为它使用了会阻塞线程的 select 系统调用,因此 IO 多路复用只能称为异步阻塞 IO 模型,而不是真正的异步 IO 模型。

⑥ IO 多路复用是指内核一旦发现进程指定的一个或多个 IO 条件准备读取,就通知该进程。

3.3.3 IO 多路复用模型使用场景

① 当客户端处理多个描述符时(一般是交互式输入和网络套接口),必须使用 IO 复用模型。

② 当一个客户端同时处理多个套接字时(此情况很少出现)。

③ 当一个 TCP 服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到 IO 复用模型。

④ 当一个服务器既要处理监听套接字,又要处理 UDP,一般要使用 IO 复用。

⑤  当一个服务器要处理多个服务或多个协议,一般要使用 IO 复用。

3.4 信号驱动 IO 模型

3.4.1 信号驱动 IO 模型图示

3.4.2 信号驱动 IO 模型说明

① 信号驱动 IO:Single-driven I/O。

② 用户进程可以通过 sigaction 系统调用注册一个信号处理程序,然后主程序可以继续向下执行,当有 IO 操作准备就绪时,由内核通知触发一个 SIGIO 信号处理程序执行,然后将用户进程所需要的数据从内核空间拷贝到用户空间。

③ 此模型的优势在于等待数据报到达期间进程不被阻塞,用户进程可以继续执行,只要等待来自信号处理函数的通知。

④ 该模型并不常用。

3.5 异步 IO 模型

3.5.1 异步 IO 模型图示

3.5.2 异步 IO 模型说明

① 异步 IO 与信号驱动 IO 最主要的区别是信号驱动 IO 是由内核通知何时可以进行 IO 操作,而异步 IO 则是由内核告诉用户线程 IO 操作何时完成。信号驱动 IO 当内核通知触发信号处理程序时,信号处理程序还需要阻塞在从内核空间缓冲区拷贝数据到用户空间缓冲区这个阶段,而异步 IO 直接是在第二个阶段完成的,内核直接通知用户线程可以进行后续操作了。

② 相比于 IO 多路复用模型,异步 IO 并不十分常用,不少高性能并发服务程序使用 IO 多路复用模型 + 多线程任务处理的架构基本可以满足需求。目前操作系统对异步 IO 的支持并非特别完善,更多的是采用 IO 多路复用模型模拟异步 IO 的方式(IO 事件触发时不直接通知用户线程,而是将数据读写完毕后放到用户指定的缓冲区中)。

4. 五种 I/O 模型图示

5. I/O 模型的具体实现

主要实现方式有如下几种

Select:Linux 实现,对应 IO 复用模型。

Poll:Linux 实现,对应 IO 复用模型。

Epoll:Linux 实现,对应 IO 复用模型,具有信号驱动 IO 模型的某些特性。

Kqueue:FreeBSD 实现,对应 IO 复用模型,具有信号驱动 IO 模型的某些特性。

/dev/poll:Sun 的 Solaris 实现,对应 IO 复用模型,具有信号驱动模型的某些特性。

Iocp:Windows 实现,对应异步 IO 模型。

6. Select/Poll/Epoll 模型对比说明

6.1 对比表格


Select


Poll


Epoll


操作方式


遍历


遍历


回调


底层实现


数组


链表


哈希表


IO 效率


每次调用都进行线性遍历


每次调用都进行线性遍历


事件通知方式,回调


最大连接数


1024或2048


无上限


无上限

6.2 Selct 模型特点

① POSIX 所规定,目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,本质上是通过设置或者检查存放 FD 标签位的数据结构来进行下一步处理(FD 表示文件描述符)。

② 单个进程可监视的 FD 数量被限制,即能监听端口的数量有限。

cat /proc/sys/fs/file-max

③ 对 socket 是线性扫描,即采用轮询的方法,效率较低。

④ Select 采用了内存拷贝方法来实现内核将 fd 消息通知给用户空间,这样一个用来存放大量 fd 的数据结构,会使得用户空间和内核空间在传递该结构时复制开销大。

6.3 Poll 模型的特点

① 本质上和 select 没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个 fd 对应的设备状态。

② 其没有最大连接数的限制,原因是它是基于链表来存储的。

③ 大量的 fd 的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。

④ Poll 的特点是 “水平触发”,如果报告了 fd 后没有被处理,那么下次 poll 时会再次报告该 fd。

6.4 Epool 模型的特点

① 在 Linux 2.6 内核中提出的 select 和 poll 的增强版本。

② 支持水平触发 LT 和边缘触发 ET,最大的特点在于边缘触发,只告诉进程哪些 fd 刚刚变为就绪态,并且只会通知一次。

③ 使用 “事件”的就绪通知方式,通过 epoll_ctl 注册 fd,一旦该 fd 就绪,内核就会采用类似 callback 的回调机制来激活该 fd,epoll_wait 便可以收到通知。

④ 没有最大连接数的限制:能打开的 FD 上线远大于 1024(1G 的内存能监听约 10 万个端口)。

⑤ 效率提升:非轮询的方式,不会随着 FD 数目的增加而效率下降;只有活跃可用的 FD 才会调用 callback 函数,即 epoll 最大的优点在于它只管理活跃的连接,而跟总连接数无关。

⑥ 内存拷贝:利用 MMAP(Memory Mapping)内存映射加速与内核空间的消息传递,即 epoll 使用 MMAP 减少复制开销 。

 

原文地址:https://www.cnblogs.com/alinuxer/p/9899888.html

时间: 2024-10-10 00:07:50

Nginx 高效核心 - IO 复用模型的相关文章

线程和IO复用模型简述

一.进程&&线程 1.进程模型 (1) 单进程模型:一个进程响应多个请求 容易造成请求的阻塞 (2) 多线程模型:每个进程响应一个请求 进程量大,进切换次数过多 每个进程地址空间独立,很多空间是重复的数据,所以内存使用率低 2.线程模型(thread) LWP:light Weight Process 在Linux上称为轻量级进程:是进程内部的自运行单位,在Linux上表现形式和进程一样,占用一个端口,但管理方式不同 多核CPU使用下,线程的优势才能发挥,并行执行线程,更好的分配系统资源,

WSAEventSelect IO复用模型

1 今天帮一学习WSAEventSelect的网友排查一个测试用服务器端recv返回0的问题,出现这个问题直观判断一般是客户端socket关闭了,可是他的代码很简单并且是本机测试,通过wireshark抓包也没有发现客户端发送了FIN分节,错误码为0,一切看起来都是正常的.正当无思路时,突然想到会不会是将recv的缓冲区长度参数给设置为0了呢,后发现果然如此,代码如下: else if(event.lNetworkEvents & FD_READ) // 处理FD_READ通知消息 { if(e

多路IO复用模型--select, poll, epoll

select 1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数 2.解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型,会大大降低服务器响应效率,不应在select上投入更多精力 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct tim

7.3 5种IO模型与IO复用

5种IO模型分别如下: 1.阻塞IO模型 当上层应用app1调用recv系统调用时,如果对等方没有发送数据(缓冲区没有数据),上层app1将阻塞(默认行为,被linux内核阻塞). 当对等方发送了数据,linux内核recv端缓冲区有数据后,内核会把数据copy给用户空间.然后上层应用app1解除阻塞,执行下一步操作. 2.非阻塞IO模型 上层应用程序app2将套接字设置成非阻塞模式. 上层应用程序app2轮询调用recv函数,接收数据,若缓冲区没有数据,上层程序app2不会阻塞,recv返回值

IO复用之——epoll

一. 关于epoll 对于IO复用模型,前面谈论过了关于select和poll函数的使用,select提供给用户一个关于存储事件的数据结构fd_set来统一监测等待事件的就绪,分为读.写和异常事件集:而poll则是用一个个的pollfd类型的结构体管理事件的文件描述符和事件所关心的events,并通过结构体里面的输出型参数revents来通知用户事件的就绪状态: 但是对于上述两种函数,都是需要用户遍历所有的事件集合来确定到底是哪一个或者是哪些事件已经就绪可以进行数据的处理了,因此当要处理等待的事

IO复用之——poll

一. 关于poll 对于IO复用模型,其优点无疑是免去了对一个个IO事件就绪的等待,转而代之的是同时对多个IO数据的检测,当检测等待的事件中至少有一个就绪的时候,就会返回告诉用户进程"已经有数据准备好了,快看看是哪个赶紧处理",而对于IO复用的实现,除了可以用select函数,另外一个函数仍然支持这种复用IO模型,就是poll函数: 二. poll函数的用法 虽然同样是对多个IO事件进行检测等待,但poll和select多少还是有些不同的: 函数参数中, 先来说nfds,这个是和sel

《深入理解计算机系统》Tiny服务器4——epoll类型IO复用版Tiny

前几篇博客分别讲了基于多进程.select类型的IO复用.poll类型的IO复用以及多线程版本的Tiny服务器模型,并给出了主要的代码.至于剩下的epoll类型的IO复用版,本来打算草草带过,毕竟和其他两种IO复用模型差不太多.但今天在看Michael Kerrisk的<Linux/UNIX系统编程手册>时,看到了一章专门用来讲解epoll函数,及其IO复用模型.于是,自己也就动手把Tiny改版了一下.感兴趣的同学可以参考上述手册的下册1113页,有对于epoll比较详细的讲解. 前边针对IO

IO复用之——select

一. select 前面提到Linux下的五种IO模型中有一个是IO复用模型,这种IO模型是可以调用一个特殊的函数同时监听多个IO事件,当多个IO事件中有至少一个就绪的时候,被调用的函数就会返回通知用户进程来处理已经ready事件的数据,这样通过同时等待IO事件来代替单一等待一个IO窗口数据的方式,可以大大提高系统的等待数据的效率:而接下来,就要讨论在Linux系统中提供的一个用来进行IO多路等待的函数--select: 二. select函数的用法 首先在使用select之前,要分清在IO事件

UNPv1第六章:IO复用select&amp;poll

有些进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪(也就是说输入已准备好被读取,或者描述符已能承受更多的输出),他就通知进程,这个能力称为I/O复用 1.IO模型 5种基本I/O模型 阻塞式I/O 非阻塞式I/O I/O复用(select和poll) 信号驱动式I/O(SIGIO) 异步I/O 一个输入操作通常包括两个不同的阶段 (1)等待数据准备 (2)从内核向进程复制数据 对于一个套接口上的输入操作,第一步一般是等待数据到达网络,当分组到达时,它被拷贝到内