我正在使用popen()创建管道,并且该过程正在调用第三方工具,在极少数情况下,我需要终止该工具。

::popen(thirdPartyCommand.c_str(), "w");

如果我只是抛出异常并展开堆栈,则展开尝试尝试对不再需要其结果的第三方进程调用pclose()。但是,pclose()永远不会返回,因为它在Centos 4上受到以下堆栈跟踪的阻塞:
#0  0xffffe410 in __kernel_vsyscall ()
#1  0x00807dc3 in __waitpid_nocancel () from /lib/libc.so.6
#2  0x007d0abe in _IO_proc_close@@GLIBC_2.1 () from /lib/libc.so.6
#3  0x007daf38 in _IO_new_file_close_it () from /lib/libc.so.6
#4  0x007cec6e in fclose@@GLIBC_2.1 () from /lib/libc.so.6
#5  0x007d6cfd in pclose@@GLIBC_2.1 () from /lib/libc.so.6

有什么办法可以强制在调用之前成功调用pclose(),这样我就可以以编程方式避免这种情况的发生,因为我不再向p停止提供输入,因此挂起等待pclose()成功的情况,但永远不会成功popen()处理过程并希望放弃它的工作?

在尝试关闭文件描述符之前,是否应该以某种方式将文件结尾写入popen()文件描述符?

请注意,第三方软件正在自我 fork 。在挂起pclose()的那一刻,有四个进程,其中一个已关闭:
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
abc       6870  0.0  0.0   8696   972 ?        S    04:39   0:00 sh -c /usr/local/bin/third_party /home/arg1 /home/arg2 2>&1
abc       6871  0.0  0.0  10172  4296 ?        S    04:39   0:00 /usr/local/bin/third_party /home/arg1 /home/arg2
abc       6874 99.8  0.0  10180  1604 ?        R    04:39 141:44 /usr/local/bin/third_party /home/arg1 /home/arg2
abc       6875  0.0  0.0      0     0 ?        Z    04:39   0:00 [third_party] <defunct>

最佳答案

我在这里看到两个解决方案:

  • 简洁:您可以“手动”输入fork()pipe()execve()(或exec系列中的任何内容……),然后由您决定是否要让 child 成为僵尸。 (即是否为他们输入wait())
  • 丑陋的:如果您确定在任何给定时间仅运行此子进程之一,则可以在调用sysctl() ... yuk 之前,使用pclose()检查是否有任何使用此名称的进程在运行。

  • 我强烈建议您采用一种简洁的方法,或者您可以问问负责修复第三方工具中无限循环的人,哈哈。

    祝好运!

    编辑:

    关于您的第一个问题:我不知道。对如何使用sysctl() shoud按名称查找进程进行了一些研究,可以告诉您您需要了解的内容,我自己从来没有做过这么远的事情。

    对于第二个和第三个问题:popen()本质上是fork() + pipe() + dup2() + execl()的包装。
    fork()复制该过程,execl()用一个新的替换图像,pipe()处理过程间的通信,dup2()用于重定向输出...然后pclose()wait()删除重复的过程,这就是为什么我们在这里。

    如果您想了解更多,您应该检查this answer,我最近在其中解释了如何使用标准IPC执行简单的派生。在这种情况下,这有点复杂,因为您必须使用dup2()将标准输出重定向到管道。

    您还应该看看popen()/pclose()源代码,因为它们当然是开源的。

    最后,这是一个简短的示例,我不能说得更清楚:
    int    pipefd[2];
    
    pipe(pipefd);
    if (fork() == 0) // I'm the child
    {
        close(pipefd[0]);    // I'm not going to read from this pipe
        dup2(pipefd[1], 1);  // redirect standard output to the pipe
        close(pipefd[1]);    // it has been duplicated, close it as we don't need it anymore
        execve()/execl()/execsomething()... // execute the program you want
    }
    else // I'm the parent
    {
        close(pipefd[1]);  // I'm not going to write to this pipe
        while (read(pipefd[0], &buf, 1) > 0) // read while EOF
            write(1, &buf, 1);
        close(pipefd[1]);  // cleaning
    }
    

    与往常一样,请记住阅读手册页并检查所有返回值。

    再次祝你好运!

    09-06 20:36