我尝试使用套接字对让父进程向执行不同程序(例如grep)的子进程提供输入,然后读取结果输出。程序挂起在while循环中,该循环读取子程序执行的程序的输出。子进程将stdin和stdout欺骗到套接字对的一端,而父进程和子进程都关闭了套接字对中未使用的一端。
有趣的是,如果孩子执行了我编写的程序(好的,我从Unix环境中的史蒂文斯高级编程中抄袭了它),一切都会按预期运行。但是,如果子进程执行grep (或其他标准程序),父进程在尝试读取输出时总是挂起。我不知道是输入没有到达grep,还是grep无法确定输入的结束,或者输出不知何故丢失了。
代码如下:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <cstdio>
#include <cerrno>
#include <iostream>
using namespace std;
void
sigpipe_handler(int sig, siginfo_t *siginfo, void * context) {
cout << "caught SIGPIPE\n";
pid_t pid;
if (errno == EPIPE) {
throw "SIGPIPE caught";
}
}
int main(int argc, char** argv) {
struct sigaction sa;
memset(&sa, '\0', sizeof(struct sigaction));
sa.sa_sigaction = sigpipe_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGPIPE, &sa, NULL);
int sp[2];
socketpair(PF_UNIX, SOCK_STREAM, AF_UNIX, sp);
pid_t childPid = fork();
if (childPid == 0) {
close(sp[0]);
if (dup2(sp[1], STDIN_FILENO) != STDIN_FILENO) throw "dup2 error to stdin";
if (dup2(sp[1], STDOUT_FILENO) != STDOUT_FILENO) throw "dup2 error to stdout";
execl("/bin/grep", "grep", "-n", "namespace", (char*)NULL);
} else {
close(sp[1]);
char line[80];
int n;
try {
while (fgets(line, 80, stdin) != NULL) {
n = strlen(line);
if (write(sp[0], line, n) != n) {
throw "write error to pipe";
}
if ((n=read(sp[0], line, 80)) < 0) { // hangs here
throw "read error from pipe";
}
if (n ==0) {
throw "child closed pipe";
break;
}
line[n] = 0;
if (fputs(line, stdout) == EOF) {
throw "puts error";
}
if (ferror(stdin)) {
throw "fgets error on stdin";
}
exit(0);
}
} catch (const char* e) {
cout << e << endl;
}
int status;
waitpid(childPid, &status, 0);
}
}发布于 2010-08-29 21:12:50
您的代码挂起,因为grep的输出可能少于80个字节,并且您正在sp上发出阻塞读取。这样做的正确方法是将两个套接字都标记为非阻塞,并在它们上选择()。
在等待()之前,您还忘记了关闭(Sp),这将使您的子进程等待输入。
发布于 2012-04-25 21:23:22
您无法使用UNIX管道或套接字对子进程实现无死锁的双向通信,因为您无法控制子进程中的缓冲。
碰巧的是,无论cat的标准输出是tty、管道还是套接字,都可以信任它读取一行并立即打印出来。对于grep (以及实际上使用stdio的大多数程序),情况并非如此,它将缓冲进程内输出(在stdio缓冲区中),并延迟write()调用,直到缓冲区已满或stdio流关闭(通常是因为grep在看到EOF on input后即将退出)。
您可以使用伪tty来欺骗面向行的程序(包括grep),使其不缓冲;看看libexpect(3)。但在一般情况下,您必须为每条消息重新运行不同的子进程,这允许使用EOF来表示每条消息的结尾,并导致刷新命令(或命令管道)中的任何缓冲区。
在perlipc手册页中可以看到关于这个问题的更多信息(它适用于Perl语言中的双向管道,但是缓冲方面的考虑无论主程序使用哪种语言都适用)。
发布于 2010-08-28 00:43:37
它在cat上运行良好,所以问题出在grep上。grep输出在连接到其他设备而不是终端时可能表现不同。或者由于某种原因,它没有检测到模式。
https://stackoverflow.com/questions/3585517
复制相似问题