2

我正在 Linux 上用 C 语言编写一个程序,其中包括一个允许在远程机器上执行 shell 命令的模块。实际执行命令的最简单方法当然是简单地使用 system() 函数,或者使用 popen 然后获取输出。但是,由于与当前问题无关的其他设计要求,我选择使用更底层的方法。

所以基本上,我建立了一个管道和分叉,然后调用 execl。这一切都很完美,除了一个恼人的例外。如果要执行的 shell 命令是一个守护进程,它就不能正常工作。在这种情况下,它只是挂起。我不知道为什么。我的理解是,当守护进程启动时,它通常会分叉,然后父进程退出。由于我的应用程序有一个到父级的开放管道,因此当父级退出时,对 read() 的调用应该会失败。但相反,应用程序只是挂起。

这是一些重现问题的基本代码:


int main(int argc, char** argv)
{
        // Create a pipe and fork
        //
        int fd[2];
        int p = pipe(fd);
        pid_t pid = fork();

    if (pid > 0)
    {
            // Read from the pipe and output the result
            //
            close(fd[1]);
            char buf[1024] = { 0 };
            read(fd[0], buf, sizeof(buf));
            printf("%s\n", buf);

            // Wait for child to terminate
            int status;
            wait(&status);
    }
    else if (pid == 0)
    {
            // Redirect stdout and stderr to the pipe and execute the shell
            // command
            //
            dup2(fd[1], STDOUT_FILENO);
            dup2(fd[1], STDERR_FILENO);
            close(fd[0]);
            execl("/bin/sh", "sh", "-c", argv[1], 0);
    }

}

如果您将其与普通的 shell 命令一起使用,则该代码可以正常工作。但是,如果您尝试运行守护程序,它只会挂起,而不是按应有的方式返回到提示符。

4

4 回答 4

2

最可能的解决方案是close(fd[1]);在 execl() 之上添加。

您的程序挂起的原因是 read() 函数等待守护进程向其 stdout/stderr 写入内容。如果守护进程(包括您的程序的子进程,以及保留其 stdout/stderr 的子进程的分叉子进程)没有写入任何内容,并且至少有一个进程保持管道的可写端处于打开状态,则读取( ) 永远不会回来。但是哪个进程使管道的可写端保持打开状态?它很可能是您的程序子进程的子进程,即长期运行的守护进程。虽然它可能已经调用了close(0);并且close(1);在守护自己时,很可能它还没有调用close(fd[1]);,所以管道的可写端仍然是打开的。

于 2009-04-30T23:45:37.137 回答
0

我认为您应该在等待读取完成时收到 SIGPIPE 信号,因为管道的另一端已关闭。你对信号做了什么不寻常的事情吗?我建议您使用 strace 命令运行您的代码。

于 2009-04-29T06:49:37.460 回答
0

您的问题可能在这里:-

// 等待子进程终止 int 状态;等待(&状态);

由于子进程是一个守护进程,它不会很快终止。

此外,您的“read()”可能会挂起。在放弃任何显示输出的尝试之前,您将不得不决定等待多长时间。

于 2009-04-29T03:42:51.123 回答
0

由于子进程是一个守护进程,它不会很快终止。

你确定吗?当然,我同意守护进程不会很快终止——但是当守护进程启动时,它会分叉,这样子进程就可以将自己与终端分离,然后父进程退出。由于 wait() 系统调用正在等待父守护进程,它应该退出。

无论如何,如果没有调用 wait(),也会出现同样的问题。

另外,为什么 read() 没有得到 EOF?read() 正在从与父守护进程连接的开放管道中读取。因此,当父守护进程退出时,read() 应该立即返回一个 EOF。

于 2009-04-29T03:52:20.430 回答