当前位置: 首页 > 知识库问答 >
问题:

从子进程中同时单独捕获stdout/stderr会导致错误的总顺序(libc/unix)

甄坚白
2023-03-14

我正在编写一个库,它应该在子进程中执行一个程序,捕获输出,并以逐行(字符串向量)的方式使输出可用。有一个用于STDOUT的向量,一个用于STDERR,还有一个用于“STDCOMBINED”,即所有输出按程序打印的顺序。子进程通过两个管道连接到父进程。一根用于标准输出,一根用于标准输出。在父进程中,我从管道的读取端读取,在子进程中,我将STDOUT/STDERR插入管道的写入端。

我的问题是:我想捕获STDOUT、STDERR和“STDCOMBINED”(=按它们出现的顺序)。但组合向量中的阶数与原始阶数不同。

我的方法是:迭代,直到两个管道都显示EOF,并且子进程退出。在每次迭代中,我从标准输出读取一行(或EOF),从标准输出读取一行(或EOF)。到目前为止,这是可行的。但是当我在父进程中捕获行时,STDOUT和STDERR的顺序与在shell中执行程序并查看输出的顺序不同。

为什么会这样?我该如何解决?这有可能吗?我知道在子进程中,我可以将STDOUT和STDERR都重定向到单个管道,但我需要分别使用STDOUT和STDERR,并且“STDCOMBINED”。

PS:我熟悉libc/unix系统调用,比如dup2()、pipe(),等等。因此我没有发布代码。我的问题是关于一般方法,而不是特定语言中的编码问题。我正在对原始libc绑定进行防锈处理。

PPS:我做了一个简单的测试程序,它混合了5个标准输出和5个标准输出消息。这足以重现问题。


共有2个答案

邹京
2023-03-14

我认为严格做你想做的事是不可能的。

如果您想一想在交互式shell中运行命令时是如何完成的,那么会发生的是stdout和stderr都指向同一个文件描述符(TTY),因此通过对同一个文件进行同步,总顺序是正确的。

为了进行说明,想象一下如果子进程有两个完全独立的线程,一个只向stderr写入,另一个只向stdout写入,会发生什么情况。总排序将取决于调度器如何决定调度这些线程,如果您想捕获这些线程,则需要将这些线程与某些内容同步。

当然,在将任何内容写入stderr之前,有些东西可以向stdout写入数千行。

有两种方法可以将您的需求放松到可行的程度:

>

  • 让用户传递一个标志,放弃单独的stdout和stderr流,以支持正确的stdcombined,然后将两者重定向到单个文件描述符。在执行该过程之前,可能需要更改缓冲设置(如stdbuf)。

    假设stdout和stderr是“合理交错的”,Nate Eldredge指出了这一假设,在这种情况下,您可以使用@Unslander Monica的答案。

  • 杭永安
    2023-03-14

    在每次迭代中,我从STDOUT中读取一行(或EOF),从STDERR中读取一行(或EOF)。

    这就是问题所在。这只会捕获正确的顺序,如果这正是子进程中的输出顺序。

    您需要捕捉beast的异步特性:使管道endpoint非阻塞,在管道上选择,并在返回时读取存在的任何数据。然后,您将捕获输出的正确顺序。当然,现在你不能只读取“一行”:你必须读取任何可用的数据,这样你就不会阻塞,并维护一个每管道缓冲区,在那里添加新数据,提取存在的任何行,将未处理的输出推到开头,然后重复。您也可以使用循环缓冲区来保存一点memcpy,但这可能不是很重要。

    既然你是在Rust中这样做的,我想已经有一个很好的异步反应模式可以利用了(我想我被围棋宠坏了,把希望投射在毫无戒心的人身上)。

    *始终偏爱特定于平台的高性能原语,例如Linux上的电子投票、Solaris上的/dev/投票投票集

    另一种可能是使用LD_PRELOAD启动目标进程,使用一个专用库,它接管glibc的POSIX写入,检测对管道的写入,并通过在数据包前面添加一个头来封装此类写入(仅限于写入),该头中存储有(原子更新的)进程范围的递增计数器以及写入的大小。这样的头可以在管道的另一端轻松解码,以对写入进行重新排序,从而获得更高的成功几率。

     类似资料:
    • 问题内容: 我想捕获并显示通过Python的子流程调用的流程的输出。 我以为我可以将文件状对象作为参数stdout和stderr传递 我可以看到它访问了属性-因此它正在处理对象。但是,永远不会调用该方法。我的方法是否完全无效或我只是缺少什么? 更新: 我还想工作的是ANSI控制字符,用于移动光标并覆盖以前的输出内容。我不知道这是否是正确的术语,但这是我的意思的一个示例:我正在尝试使一些GIT自动化

    • 问题内容: 考虑下面的C程序(test.c): 哪个应该打印一行到stdout,一行到stderr,然后等待用户输入,然后另一行到stdout,另一行到stderr。很基本!当编译并在命令行上运行时,完成后程序的输出(接收到getchar()的用户输入): 当尝试使用带有以下代码的nodejs将这个程序作为子进程生成时: 输出如下所示: 与在终端中运行./test时看到的输出非常不同。这是因为当由

    • 问题内容: 简短版本: 是否可以将sdout和stderr记录在通过ssh远程执行的命令的本地端上,其顺序与在远程主机上输出的顺序相同?如果是这样,怎么办? 长版: 我试图记录远程执行的SSH命令(使用Jsch)的标准和错误输出,其顺序与远程命令的输出顺序相同。换句话说,如果远程命令将“ a”写入stdout,然后将“ b”写入stderr,然后将“ c”写入stdout,则我希望客户端(本地)端

    • 我在我的应用程序中使用log4j来记录具有不同日志级别的某些包。 但是,stderr和stdout似乎没有在这个日志文件中捕获,而是在应用程序开始时将它们打印在文件中 是否可以在log4j中捕获stdout和stderr(例如,stdout作为info,stderr作为WARN/ERROR)以及out中。日志文件? log4j的设置如下: 更新: stackoverflow错误的StackTrac

    • 我已经尝试了很多方法,以“德语”样式在JTable中显示逗号而不是点。在我的JTable类中,我编写了一个方法,用于循环传递的结果集,以填充DefaultTableModel并直接返回模型或JTable。 如果我将一个BigDecimal转换为一个字符串,用逗号替换一个点,结果将在我的JTable中正确显示-但是我的排序序列不正确,因为值将被排序为字符串而不是BigDecimal... 另一个解决

    • 嗨,我是一个初学者,正在创建简单的wordpress模板。在我的函数中,除了粗体的代码行之外,其他一切都可以工作。php文件(wp_enqueue('jquery');)。当我排除它时,错误消失了,但我在查看源代码时没有看到wordpress添加的JQuery。不确定这是代码问题还是Jquery版本问题。我尝试添加JQuery迁移插件,但仍然出现相同的错误。我在本地机器上运行所有这些由拉拉贡设置。