linux AIO (异步IO) 那点事儿

2023-10-26

在高性能的服务器编程中,IO 模型理所当然的是重中之重,需要谨慎选型的,对于网络套接字,我们可以采用epoll 的方式来轮询,尽管epoll也有一些缺陷,但总体来说还是很高效的,尤其来大量套接字的场景下;但对于Regular File 来说,是不能够用采用 poll/epoll 的,即O_NOBLOCK 方式对于传统文件句柄是无效的,也就是说我们的 open ,read, mkdir 之类的Regular File操作必定会导致阻塞.在多线程、多进程模型中,可以选择以同步阻塞的方式来进行IO操作,任务调度由操作系统来保证公平性,但在单进程/线程模型中,以nodejs 为例 ,假如 我们需要在一个用户请求中处理10个文件: 

function fun() {

 fs.readFileSync();

 fs.readFileSync();

 …

 }

这时候进程至少会阻塞10次,而这可能会导致其他的上千个用户请求得不到处理,这当然是不能接受的. 

Linux AIO 早就被提上议程,目前比较知名的有 Glibc 的 AIO   与 Kernel Native AIO 
Glibc AIO:  http://www.ibm.com/developerworks/linux/library/l-async/  
Kernel Native AIO:  http://lse.sourceforge.net/io/aio.html  

我们用Glibc 的AIO 做个小实验,写一个简单的程序:异步方式读取一个文件,并注册异步回调函数: 
int  main()

{

struct aiocb my_aiocb;

fd = open("file.txt", O_RDONLY);

...

my_aiocb.aio_sigevent.sigev_notify_function = aio_completion_handler;

…

ret = aio_read(&my_aiocb);

…

write(1, "caller thread\n", 14);

sleep(5);

}



void aio_completion_handler(sigval_t sigval)

{

write(1, "callback\n", 9);

struct aiocb *req;

...

req = (struct aiocb *)sigval.sival_ptr;

printf("data: %s\n" ,req->aio_buf);

return;

}

我们用 strace 来跟踪调用,得到以下结果 (只保留主要语句): 

23908 open("file.txt", O_RDONLY)        = 3 
23908 clone(...) = 23909 
23908 write(1, "caller thread\n", 14)   = 14 
23908 nanosleep({5, 0},  <unfinished ...> 
... 
23909 pread(3, "hello, world\n", 1024, 0) = 13 
23909 clone(..)= 23910 
23909 futex(0x3d3a4082a4, FUTEX_WAIT_PRIVATE, 1, {0, 999942000} <unfinished ...> 
... 
23910 write(1, "callback\n", 9)         = 9 
23910 write(1, "data: hello, world\n", 19) = 19 
23910 write(1, "\n", 1)                 = 1 
23910 _exit(0)                          = ? 
23909 <... futex resumed> )             = -1 ETIMEDOUT (Connection timed out) 
23909 futex(0x3d3a408200, FUTEX_WAKE_PRIVATE, 1) = 0 
23909 _exit(0)                          = ? 
23908 <... nanosleep resumed> {5, 0})   = 0 
23908 exit_group(0)                     = ? 

在Glibc AIO 的实现中, 用多线程同步来模拟 异步IO ,以上述代码为例,它牵涉了3个线程, 
主线程(23908)新建 一个线程(23909)来调用 阻塞的pread函数,当pread返回时,又创建了一个线程(23910)来执行我们预设的异步回调函数, 23909 等待23910结束返回,然后23909也结束执行.. 

实际上,为了避免线程的频繁创建、销毁,当有多个请求时,Glibc AIO 会使用线程池,但以上原理是不会变的,尤其要注意的是:我们的回调函数是在一个单独线程中执行的. 
Glibc AIO 广受非议,存在一些难以忍受的缺陷和bug,饱受诟病,是极不推荐使用的. 
详见: http://davmac.org/davpage/linux/async-io.html  

在Linux 2.6.22+ 系统上,还有一种 Kernel AIO 的实现,与Glibc 的多线程模拟不同 ,它是真正的做到内核的异步通知,比如在较新版本的Nginx 服务器上,已经添加了AIO方式 的支持. 

http://wiki.nginx.org/HttpCoreModule  
aio 
syntax: aio [on|off|sendfile] 
default: off 
context: http, server, location 
This directive is usable as of Linux kernel 2.6.22. For Linux it is required to use directio,  this automatically disables sendfile support

location /video { 
aio on; 
directio 512; 
output_buffers 1 128k; 


听起来Kernel Native AIO 几乎提供了近乎完美的异步方式,但如果你对它抱有太高期望的话,你会再一次感到失望. 

目前的Kernel AIO 仅支持 O_DIRECT 方式来对磁盘读写,这意味着,你无法利用系统的缓存,同时它要求读写的的大小和偏移要以区块的方式对齐,参考nginx 的作者 Igor Sysoev 的评论:  http://forum.nginx.org/read.php?2,113524,113587#msg-113587  

nginx supports file AIO only in 0.8.11+, but the file AIO is functional 
on FreeBSD only. On Linux AIO is supported by nginx only on kerenl 
2.6.22+ (although, CentOS 5.5 has backported the required AIO features). 
Anyway, on Linux AIO works only if file offset and size are aligned 
to a disk block size (usually 512 bytes) and this data can not be cached 
in OS VM cache (Linux AIO requires DIRECTIO that bypass OS VM cache). 
I believe a cause of so strange AIO implementaion is that AIO in Linux 
was developed mainly for databases by Oracle and IBM. 

同时注意上面的橙色字部分,启用AIO 就会关闭sendfile -这是显而易见的,当你用Nginx作为静态服务器,你要么选择以AIO 读取文件到用户缓冲区,然后发送到套接口,要么直接调用sendfile发送到套接口,sendfile 虽然会导致短暂的阻塞,但开启AIO 却无法充分的利用缓存,也丧失了零拷贝的特征 ;当你用Nginx作为动态服务器,比如 fastcgi + php 时,这时php脚本中文件的读写是由php 的 文件接口来操作的,这时候是多进程+同步阻塞模型,和文件异步模式扯不上关系的. 

所以现在Linux 上,没有比较完美的异步文件IO 方案,这时候苦逼程序员的价值就充分体现出来了,libev 的作者 Marc Alexander Lehmann 老大就重新实现了一个AIO library : 

http://software.schmorp.de/pkg/libeio.html  

其实它还是采用线程池+同步模拟出来的,和Glibc 的 AIO 比较像,用作者的话说,这个库相比与Glibc 的实现,开销更小,bug更少(不然重新造个轮子还有毛意义呢?反正我是信了) ,不过这个轮子的代码可读性实在不敢恭维,Marc 老大自己也说了:Currently in BETA! Its code, documentation, integration and portability quality is currently below that of libev, but should soon be ready for use in production environments. 

(其实libev代码和文档可读性也不咋地,貌似驱动内核搞多了都这样?)好吧,腹诽完了,我们还是阅读下它的源码 ,来稍微分析一下它的原理: 

(这个文章的流程图还是蛮靠谱的: http://cnodejs.org/blog/?p=244   ,此处更详细的补充一下下) 

int eio_init (void (*want_poll)(void), void (*done_poll)(void)) 

初始化时设定两个回调函数,它有两个全局的数据结构 : req 存放请求队列,res 存放已经完成的队列 当我,当你提交一个异步请求时(eio_submit),其实是放入req队列中,然后向线程池中处于信号等待的线程发送信号量(如果线程池中没有线程就创建一个),获得信号的线程会执行如下代码: 
ETP_EXECUTE (self, req);

X_LOCK (reslock);

++npending;

if (!reqq_push (&res_queue, req) && want_poll_cb)

want_poll_cb ();

X_UNLOCK (reslock);

ETP_EXECUTE 就是实际的阻塞调用,比如read,open,,sendfile之类的,当函数返回时,表明操作完成,此时加锁方式向完成队列添加一项 ,然后调用 want_pool ,这个函数是我们eio_init时候设置的,然后释放锁。 

注意:每次完成任务时,都要调用want_poll ,所以这个函数应该是线程安全且尽量短促,实际上我们为了避免陷入多线程的泥淖,我们往往配合eio使用事件轮询机制,比如:我们创建一对管道,我们把“读”端的管道加入 epoll 监控结构中,want_poll 函向“写”端管道写数入一个字节或字长 ,所以当下次epoll_wait 返回时,我们会执行 “读” 端管道的 回调函数,类似如下: 
void r_pipe_cb(){

...

eio_poll();

}

在eio_poll 中 有类似以下代码: 
for(;;){

X_LOCK (reslock);

req = reqq_shift (&res_queue);

if (req){

if (!res_queue.size && done_poll_cb)

done_poll_cb ();

}

X_UNLOCK (reslock);

res = ETP_FINISH (req);

...

if(empty) break;



}

eio_poll 函数就是从完成队列res 依次shift ,依次执行我们的回调函数(ETP_FINISH 就是执行用户回调),在取出完成队列的最后一项但还没有执行用户回调之前,调用我们设定的done_poll ,对res队列的操作当然也是加锁的,注意此时我们自定义的异步回调函数是在我们的主线程中执行的!这才是我们的最终目的! 

在eio 线程池中,默认最多4个线程,在高性能的程序中,过多的进程/线程往往也是一个瓶颈, 
寄存器的进出栈还是其次,进程虚存地址切换、各级cache 的miss ,这才是最昂贵的,所以,最理想的情形就是:有几个cpu ,就有同样数目的active  线程/进程,但因为io线程往往会陷入sleep模式,所以,还是需要额外的待切换的线程的,作为经验法则,线程池的数量最好是cpu 的数目 X  2 (参见windows 核心编程 IOCP卷). 

libeio 虽不完美,但目前还是将就着用用吧 ... 


FROM:  https://cnodejs.org/topic/4f16442ccae1f4aa270010a7/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux AIO (异步IO) 那点事儿 的相关文章

  • 【Linux学习】epoll详解

    什么是epoll epoll是什么 按照man手册的说法 是为处理大批量句柄而作了改进的poll 当然 这不是2 6内核才有的 它是在2 5 44内核中被引进的 epoll 4 is a new API introduced in Linu
  • 网络层:IP协议

    本博文分享的是网络层的IP协议 从IP协议的基本概念 协议格式开始分析并分享出来 IP协议的基本概念 不同于讨论TCP UDP时只讨论通信主机之间的关系 在讨论IP协议中 会加上主机之间的网络来一起进行讨论分析 主机 一般配有IP地址 路由
  • C++在线五子棋对战(网页版)项目:websocket协议

    目标 认识理解websocket协议 websocket切换过程和websocket协议格式 认识和学会使用websocketpp库常用接口 了解websocketpp库搭建服务器流程 认识和学会使用websocketpp库bin接口 最后
  • 使用sersync实现数据实时同步

    使用sersync实现数据实时同步 sersync诞生过程 部署前提 配置rsync服务端 部署sersync 配置sersync的path变量 修改sersync配置文件 sersync常用参数 使用服务文件实现开机自启动 实时同步服务d
  • webbench剖析

    webbench 其为linux上一款web性能压力测试工具 它最多可以模拟3万个并发连接数来测试服务器压力 其原理为fork多个子进程 每个子进程都循环做web访问测试 子进程将访问的结果通过管道告诉父进程 父进程做最终结果统计 其主要原
  • Linux下socket异步通讯聊天程序

    Linux下socket异步通讯聊天程序 转 original from http yangqi org linux socket asynchronous im system Posted by yangqi 2010年02月17日 We
  • 【Linux网络(C++)】——网络套接字(TCP/UDP编程模型)多进程,多线程,线程池服务器开发(画图解析)

    目录 一 套接字基本概念 IP地址 TCP和UDP协议 端口号 端口号vs 进程pid 网络字节序 本地字节序转换成网络字节序 网络字节序转换为本地字节序 二 套接字的基本操作 socket的创建 域 domain 类型 type 协议 P
  • socket阻塞与非阻塞,同步与异步、I/O模型

    socket阻塞与非阻塞 同步与异步 作者 huangguisu 1 概念理解 在进行网络编程时 我们常常见到同步 Sync 异步 Async 阻塞 Block 非阻塞 Unblock 四种调用方式 同步 所谓同步 就是在发出一个功能调用时
  • Linux网络通信总结

    网络IO之阻塞 非阻塞 同步 异步 单播 多播 组播 广播 多路复用POLL SELECT epoll 超时 read write accept connect 超时 实现 1 用select来设置超时机制 2 使用setsockopt 函
  • 基于epoll的聊天室程序

    epoll相对于poll和select这两个多路复用的I O模型更加的高效 epoll的函数很简单 麻烦的地方在于水平出发和边沿触发 用张图来说明下 ET 边沿 只是在状态反转时触发 比如从不可读到可读 而LT 水平 就是如果可读 就会一直
  • linux系统PXE自动装机无人值守

    PXE高效批量网络装机 PXE服务允许客户机通过网络从远程服务器下载引导镜像 并安装文件或整个操作系统 必须满足的前提条件 客户机的网卡支持PXE协议 且主板支持网络引导 网络中有一台DHCP服务器为客户机分配地址 指定引导文件位置 服务器
  • Linux网络和安全:配置、远程访问与防御指南

    文章目录 Linux 网络和安全 引言 网络配置 IP地址配置 配置网络接口 防火墙设置 安全性加强 Linux网络配置及端口管理 网络配置命令 端口管理 防火墙和安全性设置 防火墙管理工具 安全性设置 Linux远程访问技术 SSH和VP
  • Linux网络编程一步一步学 - 目录(汇总)zhuan

    非常好的关于Linux网络编程的文章 我也是无意中在别人的博客里发现的 在网上搜了一下 这类的文章总共还有很多篇 最后我找到了原作者的姓名 周立发 以及作者的博客空间 本人坚决支持原创 在此将其文章进行了汇总 并且空间首页加入了他的博客链接
  • 组播技术

    1 概述 1 1 产生背景 传统的IP通信有两种方式 一种是在源主机与目的主机之间点对点的通信 即单播 另一种是在源主机与同一网段中所有其它主机之间点对多点的通信 即广播 如果要将信息发送给多个主机而非所有主机 若采用广播方式实现 不仅会将
  • TCP通信流程解析

    B S通信简述 整个计算机网络的实现体现为协议的实现 TCP IP协议是Internet的核心协议 HTTP协议是比TCP更高层次的应用层协议 HTTP HyperText Transfer Protocol 超文本传输协议 是互联网上应用
  • linux c socket之异步IO

    cpp view plain copy File UDPEchoClient TimeOut c Author 云守护 include
  • 高并发的epoll+多线程

    epoll是linux下高并发服务器的完美方案 因为是基于事件触发的 所以比select快的不只是一个数量级 单线程epoll 触发量可达到15000 但是加上业务后 因为大多数业务都与数据库打交道 所以就会存在阻塞的情况 这个时候就必须用
  • linux非阻塞socket教程

    本文并非解释什么是非阻塞socket 也不是介绍socket API的用法 取而代替的是让你感受实际工作中的代码编写 虽然很简陋 但你可以通过man手册与其它资源非富你的代码 请注意本教程所说的主题 如果细说 内容可以达到一本书内容 你会发
  • 【IP协议(一)】——IP数据报格式及其含义,IP数据报的切分

    个人主页 努力学习的少年 版权 本文由 努力学习的少年 原创 在CSDN首发 需要转载请联系博主 如果文章对你有帮助 欢迎关注 点赞 收藏 一键三连 和订阅专栏哦 IP数据报格式 版本 占4位 指ip协议的版本 首部长度 表示IP数据报中报
  • 【网络自定向下学习】——TCP报文段的详细解析

    个人主页 努力学习的少年 版权 本文由 努力学习的少年 原创 在CSDN首发 需要转载请联系博主 如果文章对你有帮助 欢迎关注 点赞 收藏 一键三连 和订阅专栏哦 目录 一 Tcp报文段的结构 二 首部长度 三 窗口大小 四 序列号和确认序

随机推荐

  • 添加序号_Excel——合并单元格添加序号

    点击上方关注我们获取更多 在工作中 为了数据便于查看 经常需要将内容相同的单元格进行合并 在进行了批量合并单元格后 如何给合并单元格添加序号成为又一难点 今天就来和大家分享一下在EXCEL中如何给合并单元格添加序号 以下表数据 城市销售数据
  • UVA-806 空间结构 题解答案代码 算法竞赛入门经典第二版

    GitHub jzplp aoapc UVA Answer 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 一道遍历四叉树的题目 在遍历的同时还要记住路径 做一些额外的操作 题目本身并不难 但是格式要求较多 比如输出路径时12个就换行 不
  • 恶意代码检测理论(静态与动态分析基础)

    什么是恶意代码 恶意代码 malicious code 又称为恶意软件 malicious software Malware 是能够在计算机系统中进行非授权操作的代码 恶意代码类型 1 蠕虫或计算机病毒 可以自我复制和感染其他计算机的恶意代
  • Linux逆向 - Mirai-上

    Mirai Mirai 变种 资料 源码 Mirai源码 xd0ol1 Mirai 源码分析 https paper seebug org 142 绿盟 MIRAI源码分析报告 http blog nsfocus net mirai sou
  • C#中结构体排序方法(Array.sort() + ICompare)

    感觉C 比C 麻烦许多 资料也少 找了半天竟然没有找到一个能用的结构体排序 这是待排序的结构体 public struct la public int id public int sb 首先 C 需要调用一个空间 类似头文件 using S
  • 自学移动端(APP)自动化测试

    国庆已经过去了 新的一周已经开始了 各地疫情又开始出现复发了 我们能做的就是做好个人防护 去哪报备 到哪扫码亮码 主动做核酸 随时做好家里存储至少一周的食物 拒绝过度防疫 拒绝过度恐慌 拒绝过度焦虑 有工作的最起码还有钱能到手 没有工作的可
  • 万字长文深度剖析AIGC技术!(网络架构&自监督)

    作者 派派星 编辑 CVHub 点击下方卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 点击进入 自动驾驶之心 全栈算法 技术交流群 前景回顾 Welcome to back 在 万字长文带你解读AIGC入门篇 一文中 我们详
  • 计算机网络生活应用,浅谈计算机网络在生活中的应用

    摘要 进入21世纪科技高速发展 特别是计算机网络的进一步发展 计算机的应用更为普遍 计算机网络的应用已经渗透到社会的各个领域 正在日益改变着传统的工作 学习和生活的方式 推动着社会的科技发展 资源的共享 通信 这两种计算机网络最基本的功能在
  • elementUI的表格多选功能之规定禁止选择

    elementUI的el table表格多选功能之禁用多选 在进行表格的多选的时候我们会碰到那种 只允许部分内容可以被选择 不符合的要禁用多选框 这个时候就要用到elementUI el table的selectable 所以我们可以这样写
  • mysql导入sql文件、数据库时报错ERROR: ASCII '\0' appeared in the statement

    window环境下mysql导入sql文件时报错 ERROR ASCII 0 appeared in the statement 错误原因 文件编码不正确 解决办法 下载UltraEdia对文件进行转码 如果是使用powershell导出的
  • 计算机网络笔记整理2——物理层

    点此链接可跳转到 计算机网络笔记整理 目录索引页 参考书籍 计算机网络 第八版 谢希仁编著 文章目录 点此链接可跳转到 计算机网络笔记整理 目录索引页 物理层的基本概念 物理层接口的基本特性 数据通信的基础知识 信道的极限容量 信道能够通过
  • AODV协议概述

    AODV是由Nokia研究中心的Charles E Perkins和加利福尼亚大学Santa Barbara的Elizabeth M Belding Roryer以及Cincinnati大学Samir R Das等共同开发 已经被 IETF
  • JDBC总结

    JDBC 规范 Java DataBase Connectivity 标题 JDK 提供 Java链接数据库的规范 采用JDBC访问数据库的基本步骤 A 载入JDBC驱动程序 B 定义连接URL C 建立连接 D 创建Statement对象
  • 光纤工程的接续、施工与测试技术规范及要点

    1 光纤接续 1 光纤接续 光纤接续应遵循的原则是 芯数相等时 要同束管内的对应色光纤对接 芯数不同时 按顺序先接芯数大的 再接芯数小的 2 光纤接续的方法有 熔接 活动连接 机械连接三种 在工程中大都采用熔接法 采用这种熔接方法的接点损耗
  • 【Unity研究】进程、线程、对象池的关系

    目录 简要概括 名词解释 实例 进程 线程 对象池 实际使用 对象池实际操作 含代码 建立主线程以外的线程方法 在主线程中运行的生命周期 在副线程中运行的生命周期 简要概括 正在运行的Unity游戏就可以看做一个进程的实例 线程是进程内的执
  • 一种信息系统免疫安全防护架构

    摘 要 随着网络的快速发展 各类社会活动的信息化日益普及 但是网络安全威胁也更加复杂多变 使得信息系统处于安全威胁风险极高的环境中 严重威胁信息的共享和获取 针对核心信息系统的安全防护 提出了一种信息系统免疫安全防护架构 针对信息系统高可用
  • c语言 水仙花数

    水仙花数是指一个N位正整数 N 3 它的每个位上的数字的N次幂之和等于它本身 本题要求编写程序 计算所有N位水仙花数 输入格式 输入在一行中给出一个正整数N 3 N 7 输出格式 按递增顺序输出所有N位水仙花数 每个数字占一行 输入样例 3
  • mysql数据库设置远程连接权限,执行grant all privileges on *.* to 'root'@'%' identified by '密码' with grant optio报错

    mysql数据库设置远程连接权限 执行grant all privileges on to root identified by 密码 with grant optio报错 ERROR 1558 HY000 Column count of
  • 华为OD机试 - 字符个数统计(C++ & Java & JS & Python)

    目录 描述 输入描述 输出描述 示例1 示例2 C python Java 描述 编写一个函数 计算字符
  • linux AIO (异步IO) 那点事儿

    在高性能的服务器编程中 IO 模型理所当然的是重中之重 需要谨慎选型的 对于网络套接字 我们可以采用epoll 的方式来轮询 尽管epoll也有一些缺陷 但总体来说还是很高效的 尤其来大量套接字的场景下 但对于Regular File 来说