【问题标题】:Why does dup() and popen() prevent my process from exiting when called from another process?为什么 dup() 和 popen() 在从另一个进程调用时阻止我的进程退出?
【发布时间】:2014-11-27 22:15:40
【问题描述】:
  • 我有一个程序,它 popen() 到另一个程序,还有 dup() 标准输出
  • 当从另一个进程(如 PHP 示例)通过 SSH 调用时,该进程不会退出。

process_test.c:

#include <stdio.h>
#include <unistd.h> 

int main() {
  int out;

  out = dup(STDOUT_FILENO);
  // close(out);

  popen("sleep 10\0", "r");
}

gcc process_test.c编译,用:运行:

  • ./a.out -> 正常退出
  • ruby -e 'system("./a.out");' -> 正常退出
  • php -r passthry("./a.out"); -> 挂起
  • ssh remotehost ./a.out -> 挂起
  • 当我不dup stdout 或关闭 dup 时,它不会挂起

这是我能找到的最短的可重现代码,它向我展示了我希望更好地理解的行为。

使用 fork/pcntl/etc 从多个 PHP 应用程序/框架中提取它需要几个小时。来衡量他们的关系,即我没有写这个或编造这个;但很明显,由于我把所有东西都拆了,它的整体感觉就消失了。

问题

  • 为什么有些调用会挂起(php、ssh)而有些则不会(ruby)?
  • 即使我关闭 fd popen 之后,我的程序也会挂起;为什么?

【问题讨论】:

    标签: c ssh posix popen dup


    【解决方案1】:

    因为当通过 ssh 调用程序时,stdoud 上的 dup 是由 ssh 生成的,所以当 ssh 尝试结束时它不能,因为 stdout 还打开了另一个通道。

    我试着给你一个更好的答案: 如果我没记错的话,stdout 是频道号 1。制作 dup 我们还有指向 stdout 的新通道 3。 当 ssh 尝试关闭通道 2 和通道 0 和 2(stdinput 和 stderr)时,ssh 无法自行关闭,因为有另一个资源占用了通道 3 或更好的标准输出。

    我希望这已经足够清楚了 加埃塔诺

    如果我按原样运行您的演示,我得到 errno = 15 或者它可能是 16,如本答案末尾所述。 但是,如果我以不同的方式运行它:

    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    
    int main() {
        int out;
        int status;
    
        out = dup(STDOUT_FILENO);
        // close(out);
        FILE *fp = popen("sleep 10\0", "r");
        status = pclose(fp);
        if (status == -1) {
                printf("pclose error: %d", errno);
        }
       close(out);
    }
    

    我不再收到错误消息。 要检查 bash 下的错误,我使用: echo $?

    这意味着您必须在退出 c 程序之前释放所有分配的资源。

    我希望就是这样。

    最好的问候 加埃塔诺

        #define ENOTBLK     15  /* Block device required */
        #define EBUSY       16  /* Device or resource busy */
    

    【讨论】:

    • 我阅读了您写的内容,但有些措辞令人困惑:“...关闭通道 2 和通道 0 和 2...”,这是什么意思?还有一个问题:为什么被调用者(ssh)依赖于这样的内部细节进程正在做什么,我的意思是它看起来如此脆弱?例如。当我在popen () 之后 关闭被欺骗的频道时,ssh 也不会退出。
    • 我重新表述了我的问题,并添加了一个转折点,即即使在 之后 popen 关闭 fd 也不会在所述情况下退出。
    • 看源代码的ssh进程就像rlogin或telnet:打开并建立与远程程序的连接后启动它,因此ssh的输入通道映射到输入通道远程程序,因此对于通道 1 和 2。当所有打开的通道(即:0、1、2)都关闭时,ssh 将关闭连接。因此,因为您复制了 1 通道以进行其他一些工作,所以 ssh 将在该过程结束时结束并关闭重复的通道 1。描述起来似乎非常复杂,我 30 年前使用了很多这样的行为....
    • 我明白了,但它没有解释为什么当我关闭频道时它确实挂起甚至,尽管之后 i>popen。你知道我在说什么吗?
    • 嗨,马克看看我的答案,让我知道再见
    猜你喜欢
    • 2020-04-04
    • 2018-07-30
    • 2012-09-28
    • 1970-01-01
    • 1970-01-01
    • 2013-03-22
    • 2018-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多