2013-10-04 208 views
5

我的操作系統類中的一個賦值需要我通過在同一程序中遞歸調用exec來構建二進制進程樹。目標是將一些任意任務分解成單獨的進程。父母應該通過未命名的管道與孩子以及父母的孩子進行交流。這個想法是,父母發送每個孩子一半的工作,這種遞歸繼續下去,直到滿足基本情況下,傳遞給每個孩子的字符串的長度爲< = 2.然後,孩子處理這些數據並將結果發回通過管道傳遞給父母。Pipe,Fork和Exec - 父子進程之間的雙向通信

爲了更好地理解雙向通信如何與c中的管道協同工作,我在創建了下面的簡單程序,然後再進行實際任務。父母從不讀取子進程中的數據。我期待輸出...

in parent |收到的消息:測試

相反,當我打印我得到...

在父母

|收到的消息:

看起來buff是空的,並且沒有從子進程讀取。有人可以解釋我在做什麼錯誤和/或

  1. 寫從父
  2. 在exec'd孩子
  3. 從寫回父母由父母讀exec'd孩子的標準方法exec'd孩子
  4. 在父母

我需要使用EXEC從exec'd孩子讀(),管(),fork()的。謝謝。

/** 
* ********************************* 
* two_way_pipes.c 
* ********************************* 
*/ 

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <math.h> 
#include <sys/time.h> 
#include <sys/types.h> 
#include <unistd.h> 

#define PARENT_READ read_pipe[0] 
#define PARENT_WRITE write_pipe[1] 
#define CHILD_WRITE read_pipe[1] 
#define CHILD_READ write_pipe[0] 

#define DEBUGGING 1 

int main(int argc, char **argv) { 
    char buff[5]; 

    // in the child process that was exec'd on the orginal call to two_way_pipes 
    if(argc == 2) { 
     read(STDIN_FILENO, buff, 4); // this should read "test" from stdin 
     buff[4] = '\0'; 
     fprintf(stdout, "%s\n", buff); // this should right "test" to stdout and be read by the parent process 
    // int the root process, the original call to two_way_pipes with no args 
    } else { 
     int pid; 
     int read_pipe[2]; 
     int write_pipe[2]; 

     pipe(read_pipe); 
     pipe(write_pipe); 

     pid = fork(); 

     // parent process 
     if(pid > 0) { 
      close(CHILD_READ); 
      close(CHILD_WRITE); 

      write(PARENT_WRITE, "test", 4); // attempting to write this to the child 

      struct timeval tv; 
      fd_set readfds; 
      tv.tv_sec = 10; 
      tv.tv_usec = 0; 
      FD_ZERO(&readfds); 
      FD_SET(PARENT_READ, &readfds); 
      select(PARENT_READ + 1, &readfds, NULL, NULL, &tv); 

      if(FD_ISSET(PARENT_READ, &readfds)) { 
       read(PARENT_READ, buff, 4); // should read "test" which was written by the child to stdout 
       buff[4] = '\0'; 
       close(PARENT_READ); 
       close(PARENT_WRITE); 
       fprintf(stderr, "in parent | message received: %s\n", buff); // "test" is not in buff 
      } 

     // child process 
     } else if(pid == 0) { 
      close(PARENT_READ); 
      close(PARENT_WRITE); 

      dup2(CHILD_READ, STDIN_FILENO); 
      dup2(CHILD_WRITE, STDOUT_FILENO); 
      close(CHILD_READ); 
      close(CHILD_WRITE); 

      char *argv2[] = {"some random arg to make sure that argc == 2 in the child", NULL}; 
      execvp("two_way_pipes", argv2); 
      _exit(0); 
     // error forking child process 
     } else { 
      fprintf(stderr, "error forking the child\n"); 
     } 
    } 
} 

更新

根據喬納森的回答我修改了ARG2陣列傳遞到execvp到...

char *argv2[] = {"two_way_pipes", "1", NULL}; 
execvp("two_way_pipes", argv2); 

這並沒有解決問題。父母仍然無法從客戶端讀取「測試」。然而,爲了迴應喬納森的回答和威廉的評論,我開始調整我的執行呼叫,並由於某種原因將其改爲下面的行顯示工作。

execl("two_way_pipes", "two_way_pipes", "1", NULL); 

我很樂意接受任何答案,解釋爲什麼execvp調用不起作用,但execl調用做了。

+0

你的exevp成功了嗎?嘗試用'perror(「execvp」)替換_exit(0)' –

+0

感謝William。我用perror(「execvp」)替換了_exit(0)調用。該錯誤消息從未打印過,但我開始調整我的EXEC調用,並出於某種原因將調用改爲execl(「two_way_pipes」,「two_way_pipes」,「1」,NULL);解決了這個問題。 –

+0

此代碼因此錯過了測試系統調用的結果。不這樣做會使測試和調試變得困難並且程序變得虛弱。請看我的答案。 – alk

回答

6

除了the issue mentioned通過Jonathon Reinhart,最有可能調用execv()失敗。

爲了檢驗這一修改這些線

execvp("two_way_pipes", argv2); 
_exit(0); 

... 
#include <errno.h> 
... 


execvp("two_way_pipes", argv2); /* On sucess exec*() functions never return. */ 
perror("execvp() failed); /* Getting here means execvp() failed. */ 
_exit(errno); 

期待接收

execvp() failed: No such file or directory 

要修正這種變化

execvp("two_way_pipes", argv2); 

execvp("./two_way_pipes", argv2); 

此外,如果孩子不是exec*()版那麼這行

read(PARENT_READ, buff, 4); // should read "test" which was written by the child to stdout 

失敗,反過來buff將不會被初始化,因此該行

fprintf(stderr, "in parent | message received: %s\n", buff); 

引發未定義的行爲。

爲了解決這個問題,通過改變

char buff[5]; 

至少正確初始化buff

char buff[5] = ""; 
+0

這真的沒什麼大不了的,因爲它始終在發生 - 但是,人們多麼頻繁地拼錯別人的名字 - 即使在同一頁面上有幾百*像素*的時候,這一點令人印象深刻。 –

+3

對不起。我懷疑如果我被命名爲安東尼,人們也會經常拼錯它:-) –

+0

@JonathonReinhart:Sry Jonathon,糾正了。 – alk

2

您的argv2中的第一項應該是可執行文件的名稱(就像您傳入的argv[0]一樣。

char *argv2[] = {"two_way_pipes", "some random arg...", NULL}; 
execvp("two_way_pipes", argv2); 

execvp man page

execv()execvp(),和execvpe()函數提供的指針爲空終止字符串表示參數列表提供給新的數組程序。按照慣例,第一個參數應指向與正在執行的文件關聯的文件名。指針陣列必須由NULL指針終止。

+0

謝謝喬納森。我根據你的回答修改了argv2,但是父母仍然沒有從exec'd孩子那裏獲取數據。 –

1

正如喬納森·萊因哈特說,你應該改變這一行:

char *argv2[] = {"some random arg to make sure that argc == 2 in the child", NULL}; 
execvp("two_way_pipes", argv2); 

到:

char *argv2[] = {argv[0], "some random arg...", NULL}; 
execvp(argv[0], argv2); 

然後它按預期工作:

echo test | ./two_way_pipes 
in parent | message received: test 

在你的節目,你寫了「two_way_pipes」,但它是不是在你的PATH所以你真的需要額外的./這樣的argv [0],然後是(」 ./two_way_pipes「)。