通过管道在 gdb 的 MI 输出上级别触发的 epoll_wait() 不通知“(gdb)\n”行的存在

Posted

技术标签:

【中文标题】通过管道在 gdb 的 MI 输出上级别触发的 epoll_wait() 不通知“(gdb)\\n”行的存在【英文标题】:Level-triggered epoll_wait() on gdb's MI output through pipe doesn't notify the existence of the "(gdb)\n" line通过管道在 gdb 的 MI 输出上级别触发的 epoll_wait() 不通知“(gdb)\n”行的存在 【发布时间】:2020-05-21 21:08:18 【问题描述】:

在一个应用程序中,我生成 gdb 并将其 stdout(和其他)连接到管道。然后我在这个管道(和其他管道)上epoll_wait 收到来自 gdb 的响应的通知。

每次epoll_wait 以正返回值唤醒时(有一个 fd 可供读取),我从 gdb 的 stdout 管道中读取 一个 行(如果那是带有事件的 fd ),然后返回epoll_wait

这一切都很好,除了有时没有读取 gdb 响应的最后一行(总是"(gdb)\n"),epoll_wait 永远返回 0。如果我等待几秒钟,然后从 gdb 的 stdout 管道读取,尽管 epoll_wait 返回 0,我可以收到 "(gdb)\n" 行。

发生了什么事?该数据显然已在管道中准备好从中读取,但级别触发的 epoll 并未为其生成事件。

一些注意事项:

连接到 gdb 的 stdout 的管道是使用 O_NONBLOCK 创建的。 epoll_create1 是用 EPOLL_CLOEXEC 调用的(仅此而已),即它是级别触发的。 我使用 GNU getline() 读取一行 在每次调用getline() 之后,我clearerr() 管道的fd(我这样做是因为在测试应用程序中我注意到是否达到EOF(因为管道的另一端还没有完成整个写入行),基于 stdio 的函数在认为到达 EOF 时卡住了。我可以处理逐块读取行,所以这很好。我还尝试删除对 clearerr() 的调用,但没有效果) 如果我在每行读取之后和再次epoll_waiting 之前添加一秒延迟,epoll_wait 将立即为初始版本+许可证消息的每一行返回stdout fd,但仍然不是最后一个"(gdb)\n" 线。

【问题讨论】:

【参考方案1】:

我怀疑问题在于 C 标准库所做的缓冲。以下是我对事件时间线的猜测:

您拨打getline getline 致电read 管道中有两条线,read 返回两条线 getline 提供第一行并缓冲第二行 您拨打epoll_wait epoll_wait 阻塞,因为内核发现管道中没有数据了 epoll_wait 几秒钟后超时 你再拨打getline getline 提供了它之前缓冲的第二行

核心问题是,就内核而言,用户空间标准输入缓冲区中的数据与您已经读取和处理的数据之间没有区别。要解决此问题,切勿将 FD 提供给将其包装在 FILE * 中的任何函数,并直接使用 read 系统调用自行完成所有读取操作。

【讨论】:

嗯,有道理。我仍然无法解释为什么如果我在epoll_waits 之间添加 1s 的延迟,我仍然会为 gdb 输出的每一行得到一个事件(它最初输出像 20 行),但也许这与最后一个有关线这么短。你是对的,我应该放弃阅读FILE *。谢谢

以上是关于通过管道在 gdb 的 MI 输出上级别触发的 epoll_wait() 不通知“(gdb)\n”行的存在的主要内容,如果未能解决你的问题,请参考以下文章

gdb 管道重定向错误:(gdb) 在 fd 0 上检测到挂起

将更改推送到特定分支时不会触发多分支管道

gdb - 使用管道进行调试

gdb mi接口命令入门大全

Azure Devops 管道通过生成验证触发两次

当输入流管道传输到多个输出流时,缓冲区级别会发生啥?