对于已运行且具有已知 PID 的进程,从外部刷新管道/printf 缓冲区

对于已运行且具有已知 PID 的进程,从外部刷新管道/printf 缓冲区

我正在编写一个数据记录应用程序,所有程序的启动方式如下:

./program > out.bin

数据收集器定期汇集 stdout 输出文件并读取数据。

问题是 IO 流被缓冲,如果某些程序以每秒 1 个字节的速度输出数据,则需要很长时间(默认 4k​​B 缓冲区大小,最多需要 4k 秒)才能实际刷新数据。

我的问题是如何强制 stdout/pipe/printf 缓冲区从外部刷新,即从外部调用类似fflush(stdout).

我读过各种网站,例如关闭管道中的缓冲,但我无法禁用缓冲区,因为它对 IO 性能有巨大的影响(测量)。

我正在寻找高性能的生产解决方案,并且始终满足以下条件:

  • 程序(数据生产者)PID 始终已知
  • 输出始终是具有已知路径的文件
  • 数据记录进程具有完全root访问权限

答案1

gdb -p PID -batch -ex 'p fflush(stdout)'

与任何调试和黑客攻击一样,YMMV。

答案2

您有权访问正在运行的程序的源代码吗?

强制刷新任意可执行文件虽然理论上并非不可能,但非常困难。您需要fflush在代码中找到该函数和stdout参数,然后中断程序的执行,安排对 的调用fflush,然后继续执行。如果程序使用共享库,那么至少使第一部分变得更容易,查找fflushstdout,但您仍然需要模拟调用。另外,对于未知的二进制文件,您无法知道它是否使用了 stdio 或者是否实现了自己的缓冲机制。知道输出文件的路径对您没有帮助。

您可以尝试使用gdb,然后使用attach命令附加到进程。也许gdb可以调用该fflush函数。

如果您有程序的源代码,请实现一个刷新缓冲区的信号处理程序,并在需要刷新缓冲区时发送信号。

您可以尝试使用管道,如果输出是管道,程序可能不会缓冲。将其更改为

./program | your_program > out.bin

您的程序可以接受输入,对其进行缓冲并在收到信号时刷新缓冲区。它仍然会增加 CPU 开销,但不会增加磁盘开销。

答案3

user313992 的答案几乎是最好的方法,并且至少也可能与其他二进制文件一起使用。解释:启动gdb,没有任何输出,附加到pid,执行flush,隐式:退出。

稍微改进一下:

gdb -batch -p $PID -ex 'call (int)fflush(stdout)' -ex 'call (int)fflush(stderr)'

这样做的好处是确保正确的返回类型并且没有任何打印。只是为了记住这一点,我还添加了 stderr 的刷新。

如果内部使用其他文件句柄或者这些句柄是 GDB 无法解析的宏,则此方法将不起作用。

自己的应用程序的最佳方法是:

  • 提供一个extern用于执行刷新的 al 函数(它消除了有关宏或不同文件句柄的所有问题,并且已经可以call由 GDB 编辑,如上面的示例所示)
  • 在程序中的有用状态下调用该函数:
    • 注册一个信号处理程序,例如SIGUSR1调用该函数 - >您可以发送该信号来强制刷新
    • 轮询文件是否存在(如果存在 - 删除然后刷新)
    • 轮询消息/信号量/套接字/...

相关内容