C中异步IO浅析之五:异步IO的调试

简介:

C中异步IO浅析之五:异步IO的调试

1. 背景

异步IO函数在使用的时候,由于它本身异步的特点,在提交IO请求的时候通常是不知道是否有错误,更谈不上返回错误类型了,只有等到有对应的event生成之后,才知道这个IO请求是否成功。 那么如何判断IO请求成功完成?如果出错,如何调试呢?

2. 原理

根据《C中异步IO浅析之三:深入理解异步IO的基本数据结构》中iocb/event之间的关系,可以看到:evnet数据结构拥有一个表示当前完成的IO请求的信息的指针,同时event数据结构中又记载着为这IO请求已传输的数据长度,根据这些信息就可以判读IO是否成功完成。更具体,就是比较期望完成传输的数据长度是否和已经完成的一致,如果一致就是顺利完成,否则就失败了。

3. 过程

根据上面的分析,可以按照下面的流程来确认IO是否成功完成:

3.1 代码部分

  • iocb初始化的时候,设置iocb.data,以便让对应的event能够知道具体的IO请求起始地址和长度信息;
  • event检查的时候,如果event.res != 应该完成的IO请求长度,assert(0);

3.2 调试过程

如果上面assert触发了core,就说明异步IO执行失败,可以参考下面的步骤开始调试:

  • gdb ./binary core-file-from-above
  • p event[0].res // 确定依据传输的数量 len_done
  • 执行类似p event[0].data[0] 命令 找到期望传输的数据的长度len_expecte
  • 根据len_done的取值进行处理:

    如果gdb p/x len_done显示的是一个0xfffffffx的值,表示传输出错,通常应该检查下面几项:

  • a. iov初始化的时候某个内存起始地址非法;

  • b. iov长度非法

    如果gdb p/x len_done值正常,但是比len_expecte小 ,通常应该检查下面几项:

  • a. iov初始化指定的用到的内存区域不够,已经用完,即将越界;
  • b. iov初始化指定的物理区域不够,已经用完,即将越界;

4. 示例

下面看两个具体的例子, 这样可以加深理解。

4.1 示例一

gdb现场如下:

gdb) p events[0]
$21 = {data = 0x7fabcbe862c0, obj = 0x7fabcbe862e8, res =**5664**, res2 = 0}
(gdb) p diskIo->iocb
$22 = {data = 0x7fabcbe862c0, key = 0, __pad2 = 0, aio_lio_opcode = 7, aio_reqprio = 0, aio_fildes = 5, u = {c = {buf = 0x7fac25e8c260, nbytes = 2, offset = 73728, __pad3 = 0,
  flags = 0, resfd = 0}, v = {vec = 0x7fac25e8c260, nr = 2, offset = 73728}, poll = {events = 636011104, __pad1 = 32684}, saddr = {addr = 0x7fac25e8c260, len = 2}}}
(gdb) p diskIo
$23 = (DiskIO *) 0x7fabcbe862c0
(gdb) p diskIo[0]
$24 = {type = ioTypeR, iov = 0x7fac25e8c260, count = 2, offset = 69632, numBytes = **65536**, iocb = {data = 0x7fabcbe862c0, key = 0, __pad2 = 0, aio_lio_opcode = 7, aio_reqprio = 0,
aio_fildes = 5, u = {c = {buf = 0x7fac25e8c260, nbytes = 2, offset = 73728, __pad3 = 0, flags = 0, resfd = 0}, v = {vec = 0x7fac25e8c260, nr = 2, offset = 73728}, poll = {events = 636011104, __pad1 = 32684}, saddr = {addr = 0x7fac25e8c260, len = 2}}}, cbFunc = 0x40c797 <CbGrp_EntCb>, cbArg = 0x7fac0fe8a280, elem = {stqe_next =0x0}}`

可以看到实际已经完成IO的数据的长度是 res = 5664, 而期望传输的数据长度是 numBytes = 65536,根据上面规则检查,发现是因为这个IO vector的数据申请的内存空间小于iov->len的值,据此修改,很快通过。

4.2 示例二

gdb现场如下:

$65 = (struct io_event *) 0x7fab7c0008c0
(gdb) p events[0]
$66 = {data = 0x7fabcbe86340, obj = 0x7fabcbe86368, res = 18446744073709551602, res2 = 0}
(gdb) p events[0].obj
$67 = (struct iocb *) 0x7fabcbe86368
(gdb) p events[0].obj[0]
$68 = {data = 0x7fabcbe86340, key = 0, __pad2 = 0, aio_lio_opcode = 0, aio_reqprio = 0, aio_fildes = 6, u = {c = {buf = 0x7ffc4ede49e0, nbytes = 131072, offset = 8192, __pad3 = 0,
  flags = 0, resfd = 0}, v = {vec = 0x7ffc4ede49e0, nr = 131072, offset = 8192}, poll = {events = 1323190752, __pad1 = 32764}, saddr = {addr = 0x7ffc4ede49e0, len = 131072}}}
(gdb) p diskIo[0]
$69 = {type = ioTypeR, iov = 0x7fac25e8c280, count = 1, offset = 4096, numBytes = 131072, iocb = {data = 0x7fabcbe86340, key = 0, __pad2 = 0, aio_lio_opcode = 0, aio_reqprio = 0,
(gdb) p/x 18446744073709551602
$1 = 0xfffffffffffffff2

可以看到实际已经完成IO的数据的长度是 res = 18446744073709551602,它其实是-14 而期望传输的数据长度是 numBytes = 131072,根据上面规则检查,发现果然是地址参数初始的时候就错了。

5. 总结

异步IO调试要求深入理解最重要的两个数据结构iocb/event之间的关联和数据流程,还需要代码实现上的配合,再加上对event.res的熟练检查,才能快速定位问题,找到root cause.












本文转自存储之厨51CTO博客,原文链接: http://blog.51cto.com/xiamachao/2053722,如需转载请自行联系原作者


相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
2月前
|
并行计算 数据处理 Python
Python并发编程迷雾:IO密集型为何偏爱异步?CPU密集型又该如何应对?
在Python的并发编程世界中,没有万能的解决方案,只有最适合特定场景的方法。希望本文能够为你拨开迷雾,找到那条通往高效并发编程的光明大道。
42 2
|
3月前
|
开发框架 并行计算 算法
揭秘Python并发神器:IO密集型与CPU密集型任务的异步革命,你竟还傻傻分不清?
揭秘Python并发神器:IO密集型与CPU密集型任务的异步革命,你竟还傻傻分不清?
46 4
|
7月前
|
调度 数据库 Python
【专栏】异步IO在处理IO密集型任务中的高效性
【4月更文挑战第27天】本文介绍了Python并发编程和异步IO,包括并发的基本概念(多线程、多进程、协程),线程与进程的实现(threading和multiprocessing模块),协程的使用(asyncio模块),以及异步IO的原理和优势。强调了异步IO在处理IO密集型任务中的高效性,指出应根据任务类型选择合适的并发技术。
162 2
|
3月前
|
算法 Java 程序员
解锁Python高效之道:并发与异步在IO与CPU密集型任务中的精准打击策略!
在数据驱动时代,高效处理大规模数据和高并发请求至关重要。Python凭借其优雅的语法和强大的库支持,成为开发者首选。本文将介绍Python中的并发与异步编程,涵盖并发与异步的基本概念、IO密集型任务的并发策略、CPU密集型任务的并发策略以及异步IO的应用。通过具体示例,展示如何使用`concurrent.futures`、`asyncio`和`multiprocessing`等库提升程序性能,帮助开发者构建高效、可扩展的应用程序。
119 0
|
5月前
|
并行计算 数据处理 Python
Python并发编程迷雾:IO密集型为何偏爱异步?CPU密集型又该如何应对?
【7月更文挑战第17天】Python并发编程中,异步编程(如`asyncio`)在IO密集型任务中提高效率,利用等待时间执行其他任务。但对CPU密集型任务,由于GIL限制,多线程效率不高,此时应选用`multiprocessing`进行多进程并行计算以突破限制。选择合适的并发策略是关键:异步适合IO,多进程适合CPU。理解这些能帮助构建高效并发程序。
123 6
|
5月前
|
算法 Java 程序员
解锁Python高效之道:并发与异步在IO与CPU密集型任务中的精准打击策略!
【7月更文挑战第17天】在数据驱动时代,Python凭借其优雅语法和强大库支持成为并发处理大规模数据的首选。并发与异步编程是关键,包括多线程、多进程和异步IO。对于IO密集型任务,如网络请求,可使用`concurrent.futures`和`asyncio`;CPU密集型任务则推荐多进程,如`multiprocessing`;`asyncio`适用于混合任务,实现等待IO时执行CPU任务。通过这些工具,开发者能有效优化资源,提升系统性能。
94 4
|
5月前
|
开发框架 并行计算 .NET
从菜鸟到大神:Python并发编程深度剖析,IO与CPU的异步战争!
【7月更文挑战第18天】Python并发涉及多线程、多进程和异步IO(asyncio)。异步IO适合IO密集型任务,如并发HTTP请求,能避免等待提高效率。多进程在CPU密集型任务中更优,因可绕过GIL限制实现并行计算。通过正确选择并发策略,开发者能提升应用性能和响应速度。
109 3
|
5月前
|
开发框架 并行计算 算法
揭秘Python并发神器:IO密集型与CPU密集型任务的异步革命,你竟还傻傻分不清?
【7月更文挑战第18天】Python并发编程中,异步IO适合IO密集型任务,如异步HTTP请求,利用`asyncio`和`aiohttp`实现并发抓取,避免等待延迟。而对于CPU密集型任务,如并行计算斐波那契数列,多进程通过`multiprocessing`库能绕过GIL限制实现并行计算。选择正确的并发模型能显著提升性能。
89 2
|
5月前
|
开发框架 数据挖掘 .NET
显微镜下的Python并发:细说IO与CPU密集型任务的异步差异,助你精准施策!
【7月更文挑战第16天】在Python并发编程中,理解和区分IO密集型与CPU密集型任务至关重要。IO密集型任务(如网络请求)适合使用异步编程(如`asyncio`),以利用等待时间执行其他任务,提高效率。CPU密集型任务(如计算)则推荐使用多进程(如`multiprocessing`),绕过GIL限制,利用多核CPU。正确选择并发策略能优化应用性能。
72 2
|
5月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之在Flink算子内部使用异步IO可以通过什么办法实现
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。