簡體   English   中英

Linux 3.0:使用管道stdin / stdout執行子進程

[英]Linux 3.0: Executing child process with piped stdin/stdout

在Linux 3.0 / C ++下:

我想要一個執行以下操作的函數:

string f(string s)
{
    string r = system("foo < s");
    return r;
}

顯然上面的方法不起作用,但你明白了。 我有一個字符串s,我想傳遞作為應用程序“foo”的子進程執行的標准輸入,然后我想將其標准輸出記錄到字符串r然后返回它。

我應該使用linux系統調用或posix函數的組合?

eerpini提供的代碼無法正常工作。 請注意,例如,之后使用在父級中關閉的管道末端。 看着

close(wpipefd[1]); 

以及隨后寫入該封閉描述符。 這只是換位,但它表明此代碼從未被使用過。 以下是我測試過的版本。 不幸的是,我改變了代碼風格,所以這不被接受為eerpini代碼的編輯。

唯一的結構變化是我只重定向子進程中的I / O(注意dup2調用僅在子路徑中。)這非常重要,因為否則父進程的I / O會搞亂。 感謝eerpini的初步答案,我用它來開發這個。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define PIPE_READ 0
#define PIPE_WRITE 1

int createChild(const char* szCommand, char* const aArguments[], char* const aEnvironment[], const char* szMessage) {
  int aStdinPipe[2];
  int aStdoutPipe[2];
  int nChild;
  char nChar;
  int nResult;

  if (pipe(aStdinPipe) < 0) {
    perror("allocating pipe for child input redirect");
    return -1;
  }
  if (pipe(aStdoutPipe) < 0) {
    close(aStdinPipe[PIPE_READ]);
    close(aStdinPipe[PIPE_WRITE]);
    perror("allocating pipe for child output redirect");
    return -1;
  }

  nChild = fork();
  if (0 == nChild) {
    // child continues here

    // redirect stdin
    if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1) {
      exit(errno);
    }

    // redirect stdout
    if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {
      exit(errno);
    }

    // redirect stderr
    if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {
      exit(errno);
    }

    // all these are for use by parent only
    close(aStdinPipe[PIPE_READ]);
    close(aStdinPipe[PIPE_WRITE]);
    close(aStdoutPipe[PIPE_READ]);
    close(aStdoutPipe[PIPE_WRITE]); 

    // run child process image
    // replace this with any exec* function find easier to use ("man exec")
    nResult = execve(szCommand, aArguments, aEnvironment);

    // if we get here at all, an error occurred, but we are in the child
    // process, so just exit
    exit(nResult);
  } else if (nChild > 0) {
    // parent continues here

    // close unused file descriptors, these are for child only
    close(aStdinPipe[PIPE_READ]);
    close(aStdoutPipe[PIPE_WRITE]); 

    // Include error check here
    if (NULL != szMessage) {
      write(aStdinPipe[PIPE_WRITE], szMessage, strlen(szMessage));
    }

    // Just a char by char read here, you can change it accordingly
    while (read(aStdoutPipe[PIPE_READ], &nChar, 1) == 1) {
      write(STDOUT_FILENO, &nChar, 1);
    }

    // done with these in this example program, you would normally keep these
    // open of course as long as you want to talk to the child
    close(aStdinPipe[PIPE_WRITE]);
    close(aStdoutPipe[PIPE_READ]);
  } else {
    // failed to create child
    close(aStdinPipe[PIPE_READ]);
    close(aStdinPipe[PIPE_WRITE]);
    close(aStdoutPipe[PIPE_READ]);
    close(aStdoutPipe[PIPE_WRITE]);
  }
  return nChild;
}

由於您希望對流程進行雙向訪問,因此您必須使用管道明確地執行popen在幕后的操作。 我不確定這是否會在C ++中發生任何變化,但這是一個純粹的C示例:

void piped(char *str){
    int wpipefd[2];
    int rpipefd[2];
    int defout, defin;
    defout = dup(stdout);
    defin = dup (stdin);
    if(pipe(wpipefd) < 0){
            perror("Pipe");
            exit(EXIT_FAILURE);
    }
    if(pipe(rpipefd) < 0){
            perror("Pipe");
            exit(EXIT_FAILURE);
    }
    if(dup2(wpipefd[0], 0) == -1){
            perror("dup2");
            exit(EXIT_FAILURE);
    }
    if(dup2(rpipefd[1], 1) == -1){
            perror("dup2");
            exit(EXIT_FAILURE);
    }
    if(fork() == 0){
            close(defout);
            close(defin);
            close(wpipefd[0]);
            close(wpipefd[1]);
            close(rpipefd[0]);
            close(rpipefd[1]);
            //Call exec here. Use the exec* family of functions according to your need
    }
    else{
            if(dup2(defin, 0) == -1){
                    perror("dup2");
                    exit(EXIT_FAILURE);
            }
            if(dup2(defout, 1) == -1){
                    perror("dup2");
                    exit(EXIT_FAILURE);
            }
            close(defout);
            close(defin);
            close(wpipefd[1]);
            close(rpipefd[0]);
            //Include error check here
            write(wpipefd[1], str, strlen(str));
            //Just a char by char read here, you can change it accordingly
            while(read(rpipefd[0], &ch, 1) != -1){
                    write(stdout, &ch, 1);
            }
    }

}

有效地你這樣做:

  1. 創建管道並將stdout和stdin重定向到兩個管道的末端(請注意,在linux中,pipe()會創建單向管道,因此您需要使用兩個管道來實現)。
  2. Exec現在將啟動一個新進程,其中包含stdin和stdout管道的末尾。
  3. 關閉未使用的描述符,將字符串寫入管道,然后開始讀取進程可能轉儲到其他管道的任何內容。

dup()用於在文件描述符表中創建重復條目。 而dup2()更改描述符指向的內容。

注意:正如Ammo @在他的解決方案中所提到的,我上面提供的內容或多或少是一個模板,如果你只是試圖執行代碼它將無法運行,因為很明顯有一個exec *(函數族)缺失,所以child會在fork()之后幾乎立即終止。

Ammo的代碼有一些錯誤處理錯誤。 子進程在dup失敗后返回而不是退出。 也許孩子的副本可以替換為:

    if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1 ||
        dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1 ||
        dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1
        ) 
    {
        exit(errno); 
    }

    // all these are for use by parent only
    close(aStdinPipe[PIPE_READ]);
    close(aStdinPipe[PIPE_WRITE]);
    close(aStdoutPipe[PIPE_READ]);
    close(aStdoutPipe[PIPE_WRITE]);

暫無
暫無

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

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