【问题标题】:Why does /bin/sh hang when ran via execl on this particular script?为什么 /bin/sh 在此特定脚本上通过 execl 运行时会挂起?
【发布时间】:2019-04-19 18:35:45
【问题描述】:

你能给我一些关于以下代码的想法吗?代码运行但不退出。另一个字符串s="ls -1" 运行良好。通过sh(1) 运行shell sn-p 也可以正常工作。

#include <unistd.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    int fd[2];

    char *s = "ls -1 \"/usr/bin\" | while IFS= read -r fp\ndo\ncat <<- EOF\n\t$fp\nEOF\ndone;";
    //char *s = "ls -1";


    pipe(fd);

    switch(fork()) {
        case 0:
            close(fd[1]);
            dup2(fd[0], 0);
            execl("/bin/sh", "sh", NULL);
            close(fd[0]);
        break;
        default:
            close(fd[0]);
            write(fd[1], s, strlen(s) + 1);
            close(fd[1]);
        break;
    }

    return 0;
}

【问题讨论】:

  • 您没有在孩子中关闭足够的文件描述符。 经验法则:如果您将管道的一端dup2() 连接到标准输入或标准输出,请尽快关闭pipe() 的两个原始文件描述符。特别是,这意味着 使用任何exec*() 系列函数之前。该规则也适用于dup()fcntl()F_DUPFD
  • 在这种情况下,close(fd[0]); 需要在execl() 之前——记住,如果成功,execl() 永远不会返回(它只会在失败时返回)。你应该有一个错误报告和exit(1);execl() 之后的类似内容;目前,即使失败了,你也会报告成功。
  • @JonathanLeffler,我想知道execlclose(fd[0]); 的顺序,你的cmets 是有道理的,但是,切换它们的顺序仍然不起作用。它的行为方式相同,不会退出。
  • 查看我的回答——是的,我同意 cmets 不是您问题的解决方案,尽管 exec 之后的关闭没有执行,并且那里应该有一条错误消息。
  • 也可能与大量的 heredocs 和 tmp 文件有关。

标签: c unix pipe sh


【解决方案1】:

当我将我的 cmets 转换为答案时,我测试了提议的更改,但它并没有解决问题。一个应该有效的解决方法是在字符串末尾添加; exit,即使这无异于作弊。但是,测试也表明它没有完成;好像ls 没有终止。

我到另一个终端查看lspipe97(我的代码名称)的进程是否还在;他们不是。

  • 尝试在“挂起”进程中输入 ps

你应该得到一个正常的输出和你的提示。

因为父母不等待孩子退出,所以提示在ls 的输出中的某处丢失了(这很长并且产生的速度很慢)。将输出重定向到/dev/null,您将看到提示。

  • 最好的解决办法可能是在父进程中添加一个wait() 循环,这样在子进程完成之前它不会退出。
#include <sys/wait.h>

…
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
    ;

在调试时,您可以打印corpsestatus,以便查看发生了什么。


我观察到这个comment(没有序言)仍然有效:

close(fd[0]); 必须在 execl() 之前——记住,如果成功,execl() 永远不会返回(它只会在失败时返回)。你应该有一个错误报告和exit(1);execl() 之后的类似内容;此刻,即使失败了,你也会报告成功。

就其本身而言,Rule of Thumb 注释是有效的,但它实际上不适用于此代码 — 未关闭的管道描述符不会导致问题,即使一个应该关闭的管道描述符没有关闭。

【讨论】:

    【解决方案2】:

    @WilliamPursell 谢谢,你是对的。在这个例子中我并没有关注这一点。

    @user3629249 我知道我没有进行正确的错误检查,谢谢!

    @mosvy 它是 OpenBSD,确实是在 Mac 上运行。

    此版本有效:

    #include <string.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int
    main(int argc, char *argv[])
    {
        int fd[2];
    
        char *s = "ls -1 '/usr/bin' | while IFS= read -r fp\ndo\ncat <<- EOF\n\t$fp\nEOF\ndone;";
        //char *s = "ls -1";
    
    
        pipe(fd);
    
        switch(fork()) {
            case 0:
                close(fd[1]);
                dup2(fd[0], 0);
                execl("/bin/sh", "sh", NULL);
                close(fd[0]);
            break;
            default:
                close(fd[0]);
                write(fd[1], s, strlen(s));
                close(fd[1]);
                wait(NULL);
            break;
        }
    
        return 0;
    }
    

    wait(2) 做到了。我记得我在某个时候做过wait(2),但是我认为我搞砸了close(1) 的命令,所以它被阻塞了。

    无论如何,问题已解决,谢谢!

    【讨论】:

      猜你喜欢
      • 2017-07-31
      • 2020-09-15
      • 2021-05-17
      • 1970-01-01
      • 2018-06-01
      • 2013-01-29
      • 2021-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多