首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用socketpair的双向通信:挂起读出子进程的输出

使用socketpair的双向通信:挂起读出子进程的输出
EN

Stack Overflow用户
提问于 2010-08-27 23:06:56
回答 3查看 2.7K关注 0票数 1

我尝试使用套接字对让父进程向执行不同程序(例如grep)的子进程提供输入,然后读取结果输出。程序挂起在while循环中,该循环读取子程序执行的程序的输出。子进程将stdin和stdout欺骗到套接字对的一端,而父进程和子进程都关闭了套接字对中未使用的一端。

有趣的是,如果孩子执行了我编写的程序(好的,我从Unix环境中的史蒂文斯高级编程中抄袭了它),一切都会按预期运行。但是,如果子进程执行grep (或其他标准程序),父进程在尝试读取输出时总是挂起。我不知道是输入没有到达grep,还是grep无法确定输入的结束,或者输出不知何故丢失了。

代码如下:

代码语言:javascript
复制
#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);
  }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-08-29 21:12:50

您的代码挂起,因为grep的输出可能少于80个字节,并且您正在sp上发出阻塞读取。这样做的正确方法是将两个套接字都标记为非阻塞,并在它们上选择()。

在等待()之前,您还忘记了关闭(Sp),这将使您的子进程等待输入。

票数 3
EN

Stack Overflow用户

发布于 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语言中的双向管道,但是缓冲方面的考虑无论主程序使用哪种语言都适用)。

票数 3
EN

Stack Overflow用户

发布于 2010-08-28 00:43:37

它在cat上运行良好,所以问题出在grep上。grep输出在连接到其他设备而不是终端时可能表现不同。或者由于某种原因,它没有检测到模式。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3585517

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档