我在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命令,代码可以正常工作。但如果 你试图运行一个守护进程,它只是挂起而不是返回到 提示它应该。
答案 0 :(得分:2)
最可能的解决方案是在execl()上面添加close(fd[1]);
。
程序挂起的原因是read()函数等待守护进程向其stdout / stderr写入内容。如果守护进程(包括你的程序的子进程,以及子进程'保留stdout / stderr的分叉子进程)没有写任何内容,并且至少有一个进程保持管道的可写端打开,请阅读( )永远不会回来。但是那个过程是什么,它将管道的可写端保持打开状态?它很可能是程序的孩子的孩子,长期运行的守护进程。虽然在守护自身时可能会调用close(0);
和close(1);
,但很可能它没有调用close(fd[1]);
,因此管道的可写端仍然是打开的。
答案 1 :(得分:0)
你的问题可以在这里: -
//等待孩子终止 int状态; 等待(安培;状态);
由于子进程是一种守护,它不会很快终止。
此外,您的“read()”可能会挂起。在放弃任何显示输出的尝试之前,您必须决定等待多长时间。
答案 2 :(得分:0)
你确定吗?当然我同意守护进程不会很快终止 - 但是当一个守护进程启动时它会分叉,这样孩子就可以与终端解除关联,然后父进程退出。由于wait()系统调用正在等待父守护进程,因此它应该退出。由于子进程是一种守护,它不会很快终止。
无论如何,如果没有调用wait(),也会出现同样的问题。
另外,为什么read()得不到EOF? read()从与父守护进程连接的打开管道读取。因此,当父守护程序进程退出时,read()应立即返回EOF。
答案 3 :(得分:0)
我认为在等待读取完成时应该收到SIGPIPE信号,因为管道的另一端已关闭。你有什么不寻常的信号吗?我建议你使用strace命令运行你的代码。