2012-11-04 21 views
0

我需要從C++應用程序中運行外部程序。我需要該程序的輸出(我想在程序仍在運行時看到它),並且還需要輸入。C++外部程序IO

什麼是重定向IO的最好和最優雅的方式?它是否應該在自己的線程中運行?任何例子?

它在OSX上運行。

我實現這樣的:

ProgramHandler::ProgramHandler(std::string prog): program(prog){ 
// Create two pipes 
std::cout << "Created Class\n"; 
pipe(pipe1); 
pipe(pipe2); 

int id = fork(); 

std::cout << "id: " << id << std::endl; 

if (id == 0) 
{ 
    // In child 
    // Close current `stdin` and `stdout` file handles 
    close(fileno(stdin)); 
    close(fileno(stdout)); 

    // Duplicate pipes as new `stdin` and `stdout` 
    dup2(pipe1[0], fileno(stdin)); 
    dup2(pipe2[1], fileno(stdout)); 

    // We don't need the other ends of the pipes, so close them 
    close(pipe1[1]); 
    close(pipe2[0]); 

    // Run the external program 
    execl("/bin/ls", "bin/ls"); 
    char buffer[30]; 
    while (read(pipe1[0], buffer, 30)) { 
     std::cout << "Buf: " << buffer << std::endl; 
    } 
} 
else 
{ 
    // We don't need the read-end of the first pipe (the childs `stdin`) 
    // or the write-end of the second pipe (the childs `stdout`) 
    close(pipe1[0]); 
    close(pipe2[1]); 


    // Now you can write to `pipe1[1]` and it will end up as `stdin` in the child 
    // Read from `pipe2[0]` to read from the childs `stdout` 
} 

}

但作爲輸出我得到這個:

創建

ID:84369

ID:0

我不明白爲什麼它被稱爲兩次,爲什麼它不會第一次分叉。我在做什麼/理解錯誤。

+1

Windows或Linux或OSX或其他? –

+0

Sry忘記了:這是OSX –

回答

3

如果使用POSIX系統(如OSX或Linux),那麼你必須學會​​系統調用pipeforkclosedup2exec

你要做的是創建兩個管道,一個用於讀取外部應用程序,一個用於寫入。然後你fork來創建一個新的過程,並在子進程中,你設置管道爲stdinstdout,然後調用exec,它用一個外部程序使用新的stdinstdout文件句柄替換子進程。在父進程中,您無法讀取子進程的輸出,並寫入其輸入。

在僞代碼:

// Create two pipes 
pipe(pipe1); 
pipe(pipe2); 

if (fork() == 0) 
{ 
    // In child 
    // Close current `stdin` and `stdout` file handles 
    close(FILENO_STDIN); 
    close(FILENO_STDOUT); 

    // Duplicate pipes as new `stdin` and `stdout` 
    dup2(pipe1[0], FILENO_STDIN); 
    dup2(pipe2[1], FILENO_STDOUT); 

    // We don't need the other ends of the pipes, so close them 
    close(pipe1[1]); 
    close(pipe2[0]); 

    // Run the external program 
    exec("/some/program", ...); 
} 
else 
{ 
    // We don't need the read-end of the first pipe (the childs `stdin`) 
    // or the write-end of the second pipe (the childs `stdout`) 
    close(pipe1[0]); 
    close(pipe2[1]); 

    // Now you can write to `pipe1[1]` and it will end up as `stdin` in the child 
    // Read from `pipe2[0]` to read from the childs `stdout` 
} 

閱讀手冊頁的系統調用以得到更多的信息。您還需要添加錯誤檢查,因爲所有這些系統調用都可能失敗。

+0

我真的很喜歡你的解決方案。唯一的問題是我找不到FILENO_STDIN和exec。我需要哪個頭? –

+0

@BenediktWutzi沒有單獨的'exec'函數,而是一個函數族。見例如['exec(3)'](http://developer.apple.com/library/Mac/#documentation/Darwin/Reference/ManPages/man3/exec.3.html)獲取所有功能的列表。對於'FILENO_STDIN',你需要''頭文件。 –

+0

使用fileno(stdin) –

0

與POSIX系統不同的程序進行通信的基本方法是設置一個pipe(),然後fork()程序,close()dup()文件描述符到正確的位置,並最終以exec??()所需的可執行文件。

完成此操作後,您的兩個程序將連接到合適的流。不幸的是,這並不涉及這兩種程序的任何形式的異步處理。也就是說,您可能希望通過適當的異步和非阻塞操作來訪問創建的文件描述符(即,將各種文件描述符設置爲非阻塞和/或僅當poll()產生指示您的結果時訪問它們可以訪問它們)。但是,如果只有一個可執行文件,則可以更容易地從單獨的線程控制它。

1

那麼有一個相當標準的方法來做到這一點。一般來說,您想分叉進程並關閉子進程的標準I/O(fd 0,1)。分叉之前,先創建兩個管道,然後在關閉子標準輸入和輸出之後,使用dup將它們連接到管道。

僞代碼,只顯示連接的一端,我相信你可以找出另一端。

int main(){ 
    int fd[2]; // file descriptors 
    pipe(fd); 

     // Fork child process 
     if (fork() == 0){ 
      char buffer [80]; 
      close(1); 
      dup(fd[1]); // this will take the first free discriptor, the one you just closed. 
      close(fd[1]); // clean up 
     }else{ 
      close(0); 
      dup(fd[0]); 
      close(fd[0]); 
     }  
     return 0; 
    } 

你管建立後並等待一個選擇什麼的線程父之一,你可以調用exec爲您的外部工具,並擁有所有流動的數據。

+0

修復了它「等待某個選擇或某事,您可以調用exec爲您的外部工具並讓所有數據流動。」 – qballer

+0

啊,沒看過:) –

0

一種不同的方法(如果你也在編寫外部程序)是使用共享內存。 (僞代碼)

// create shared memory 
int l_shmid = shmget(key, size ,0600 | IPC_CREAT); 

if(l_shmid < 0) 
    ERROR 

// attach to shared memory 
dataptr* ptr = (dataptr*)shmat(l_shmid, NULL, 0600); 

// run external program 
pid_t l_pid = fork(); 

if(l_pid == (pid_t)-1) 
{ 
    ERROR 

    // detach & delete shared mem 
    shmdt(ptr); 
    shmctl(l_shmid, 
     IPC_RMID, 
     (shmid_ds *)NULL); 
    return; 
} 
else if(l_pid == 0) 
{ 
    // child: 
    execl(path, 
      args, 
      NULL); 
    return; 
} 

// wait for the external program to finish 
int l_stat(0); 
waitpid(l_pid, &l_stat, 0); 

// read from shmem 
memset(mydata, ..,..); 
memcpy(mydata, ptr, ...); 

// detach & close shared mem 
shmdt(ptr); 
shmctl(l_shmid, 
     IPC_RMID, 
     (shmid_ds *)NULL); 

您的外部程序可以以類似的方式寫入共享內存。無需管道&讀/寫/選擇等