【问题标题】:Using execl to execute a daemon使用 execl 执行守护进程
【发布时间】:2009-04-29 03:00:18
【问题描述】:

我正在 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 命令一起使用,则该代码可以正常工作。但如果 你尝试运行一个守护进程,它只是挂起而不是返回到 应有的提示。

【问题讨论】:

  • 起初我读“excel”并认为WTF! ;)

标签: c linux daemon


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    你的问题可能在这里:-

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

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

    您的“read()”也可能挂起。在放弃任何显示输出的尝试之前,您必须决定等待多长时间。

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

      • 解决此问题的一种简单方法是将 gdb 附加到程序并逐步执行程序以查看它是否挂在 wait 上。
      【解决方案4】:

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

      【讨论】:

      • 您将永远不会获得用于 read() 操作的 SIGPIPE。如果您正在读取()的管道的另一端已关闭,则 read() 最终将返回 0 指示 EOF。如果您正在写入()的管道的另一端已关闭,您将获得一个 SIGPIPE,除非您的进程忽略 SIGPIPE(例如使用信号(SIGPIPE,SIG_IGN))。在这种情况下,write() 将返回 -1,而 errno 将变为 EPIPE。