簡體   English   中英

使用套接字對的雙向通信:掛起子進程的讀取輸出

[英]bi-directional communication using socketpair: hangs reading output from child process

我正在嘗試使用套接字對讓父進程向執行不同程序(例如grep)的子進程提供輸入,然后讀取結果輸出。 程序掛在while循環中,該循環從子進程執行的程序中讀取輸出。子進程將stdin和stdout復制到套接字對的末尾,並且父級和子進程都關閉了它們對中未使用的一端。

有趣的是,如果孩子執行了我編寫的程序(好的,我從Unix環境中的Stevens Advanced Programming中刪除了該程序),那么一切都會按預期進行。 但是,如果子進程執行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);
  }
}

您無法使用UNIX管道或套接字對與子進程實現無死鎖的雙向通信,因為您無法控制子進程中的緩沖。

碰巧的是,不管它的標准輸出是tty,管道還是套接字,都可以信任cat讀取一行並立即打印它。 對於grep (實際上是大多數使用stdio的程序)而言,情況並非如此,它將在進程中緩沖輸出(在stdio緩沖區中)並推遲write()調用,直到緩沖區已滿或stdio流關閉(通常是)因為grep在輸入中看到EOF之后即將退出)。

您可以通過使用偽tty來誘使面向行的程序(包括grep )不進行緩沖。 看一下libexpect(3) 但是在一般情況下,您將不得不為每個消息重新運行一個不同的子進程,這將允許使用EOF來表示每個消息的結尾,並導致刷新命令(或命令管道)中的所有緩沖區。

perlipc手冊頁中可以找到有關此問題的更多信息(它適用於Perl中的雙向管道,但是無論主程序使用哪種語言,緩沖注意事項都適用)。

由於grep的輸出可能少於80個字節,並且您正在對sp [0]發出阻塞讀取,因此代碼將掛起。 正確的方法是將兩個套接字都標記為非阻塞,並在兩個套接字上均選擇()。

您還忘記了在wait()之前關閉(sp [0]),這將使子進程等待輸入。

它在cat上工作正常,所以問題出在grep上。 當連接到除終端以外的其他設備時,grep輸出的行為可能有所不同。 或由於某種原因未檢測到模式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM