2015-08-08 50 views
1

因此,對於我的計算機系統類的分配,我需要在程序運行時在命令行中輸入字符。發送字符從父到子進程並返回字符數到父母在C

這些字符(如abcd ef)將被存儲在argv[]中。

父母通過管道將這些字符一次一個地發送給子進程,子進程然後統計字符並忽略空格。在發送完所有字符之後,子代會返回父代要報告的字符數。

當我嘗試運行該程序,因爲它是現在,它告訴我的readIn值是4,孩子處理0字符和charCounter爲2

我覺得我如此接近,但我錯過了一些重要的東西:/在a和父進程的char數組試圖硬編碼的東西,看看它的工作,但我仍然不成功。任何幫助將不勝感激,謝謝!

// Characters from command line arguments are sent to child process 
// from parent process one at a time through pipe. 
// 
// Child process counts number of characters sent through pipe. 
// 
// Child process returns number of characters counted to parent process. 
// 
// Parent process prints number of characters counted by child process. 

#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h>   // for fork() 
#include <sys/types.h>  // for pid_t 
#include <sys/wait.h>  // for waitpid() 

int main(int argc, char **argv) 
{ 
    int fd[2]; 
    pid_t pid; 
    int  status; 
    int charCounter = 0; 
    int nChar = 0; 
    char readbuffer[80]; 
    char readIn = 'a'; 

    //char a[] = {'a', 'b', 'c', 'd'}; 

    pipe(fd); 



    pid = fork(); 

    if (pid < 0) { 
     printf("fork error %d\n", pid); 
     return -1; 
     } 
    else if (pid == 0) { 
     // code that runs in the child process 

     close(fd[1]); 
     while(readIn != 0) 
     { 
      readIn = read(fd[0], readbuffer, sizeof(readbuffer)); 

      printf("The value of readIn is %d\n", readIn); 
      if(readIn != ' ') 
      { 
       charCounter++; 
      } 
     } 
     close(fd[0]); 

     //open(fd[1]); 

     //write(fd[1], charCounter, sizeof(charCounter)); 

     printf("The value of charCounter is %d\n", charCounter); 

     return charCounter; 
     } 
    else 
    { 
     // code that runs in the parent process 

     close(fd[0]); 

     write(fd[1], &argv, sizeof(argv)); 

     //write(fd[1], &a, sizeof(a)); 

     close(fd[1]); 

     //open(fd[0]); 

     //nChar = read(fd[0], readbuffer, sizeof(readbuffer)); 

     nChar = charCounter; 

     printf("CS201 - Assignment 3 - Andy Grill\n"); 

     printf("The child processed %d characters\n\n", nChar); 


     if (waitpid(pid, &status, 0) > 0) 
     { 
      if (WIFEXITED(status)) 
      { 
      } 
      else if (WIFSIGNALED(status)) 
      { 
      } 
     } 

     return 0; 
    } 
} 
+0

提示:張貼在計算器代碼時,一定要通過4個空格所以它得到正確顯示縮進它。請參閱[編輯幫助](http://stackoverflow.com/editing-help)。 – spectras

+0

謝謝,提出改變編輯適當間距 –

回答

0

您錯用了管道。

管道是一個單向通信通道。您可以使用它將數據從父進程發送到子進程,也可以將數據從子進程發送到父進程。你不能這樣做 - 即使你在這兩個過程中保持管道的讀寫通道都是打開的,每個過程都不​​會知道什麼時候輪到從管道讀取數據(例如,你最終可能會讀到兒童應該由父母閱讀)。

將字符從父母發送到孩子的代碼看起來大部分是正確的(下面有更多詳細信息),但是您需要重新設計孩子到父母的通信。現在,您有兩個選項可以將結果從子項發送到父項:

  • 使用另一個管道。在爲孩子與父母溝通分擔之前,您設置了一個額外的管道。這使設計和代碼複雜化,因爲現在有4個文件描述符可以從2個不同的管道進行管理,並且在關閉每個文件描述符以確保進程不會掛起時需要小心。這也可能有點矯枉過正,因爲孩子只向父母發送號碼。
  • 將孩子的結果作爲退出值返回。這就是你現在正在做的,這是一個不錯的選擇。但是,您無法在父級中檢索該信息:孩子的終止狀態會告訴您處理的字符數,您可以使用waitpid(2)獲取此值,但您之前完成的操作不會看到status(其中包含您正在尋找)。

請記住,子進程有自己的地址空間。嘗試在父文件中讀取charCounter是沒有意義的,因爲父文件從未修改它。子進程獲得自己的副本charCounter,所以任何修改都只能由孩子看到。你的代碼似乎假定不然。

爲了使這更明顯,我建議將變量的聲明移到相應的進程代碼。需要在兩個進程中複製fdpid,其他變量是特定於每個進程的任務的。因此,您可以將statusnChar的聲明移至父進程特定代碼,並且您可以將charCounterreadbufferreadIn移動到子代。這將非常明顯地表明變量在每個過程中都是完全獨立的。

現在,一些更具體的說明:

  • pipe(2)可以返回一個錯誤。你忽略了返回值,你不應該這樣做。至少,您應該打印一條錯誤消息並終止,如果pipe(2)出於某種原因失敗。我也注意到你在fork(2)printf("fork error %d\n", pid);報告錯誤。這不是正確的方法:fork(2)和其他系統調用(和庫調用)在出錯時總是返回-1,並設置全局變量errno以指示原因。因此,printf()將始終打印分叉錯誤-1無論錯誤原因是什麼。這沒有幫助。此外,它將錯誤消息打印到stdout,由於多種原因,應將錯誤消息打印到stderr。所以我建議使用perror(3)代替,或手動將錯誤打印到stderrfprintf(3)perror(3)還具有將錯誤消息描述附加到所提供文本的附加好處,所以它通常是一個不錯的選擇。

例子:

if (pipe(fd) < 0) { 
    perror("pipe(2) error"); 
    exit(EXIT_FAILURE); 
} 

您使用整個代碼也可能會失敗,並再次,你忽略了(可能的)錯誤返回等功能。 close(2)可能會失敗,以及read(2)。處理錯誤,他們在那裏是有原因的。

  • 您使用readIn的方式是錯誤的。 readInread(2)的結果,它返回讀取的字符數(它應該是int)。該代碼使用readIn,就好像它是下一個閱讀的字符一樣。讀取的字符存儲在readbuffer中,而readIn會告訴您該緩衝區中有多少個字符。因此,您使用readIn循環訪問緩衝區內容並對字符進行計數。事情是這樣的:

    readIn = read(fd[0], readbuffer, sizeof(readbuffer)); 
    while (readIn > 0) { 
        int i; 
        for (i = 0; i < readIn; i++) { 
         if (readbuffer[i] != ' ') { 
          charCounter++; 
         } 
        } 
        readIn = read(fd[0], readbuffer, sizeof(readbuffer)); 
    } 
    

現在,關於父進程:

你是不是寫的人物進入管道。這是毫無意義:

write(fd[1], &argv, sizeof(argv)); 

&argvchar ***類型,並且sizeof(argv)相同sizeof(char **),因爲argv是一個char **。傳遞給函數時,數組維不保留。

您需要通過argv手動循環和寫入每個進入管道,就像這樣:

int i; 
for (i = 1; i < argv; i++) { 
    size_t to_write = strlen(argv[i]); 
    ssize_t written = write(fd[1], argv[i], to_write); 
    if (written != to_write) { 
     if (written < 0) 
      perror("write(2) error"); 
     else 
      fprintf(stderr, "Short write detected on argv[%d]: %zd/zd\n", i, written, to_write); 
    } 
} 

注意argv[0]是程序的名稱,這就是爲什麼i從1開始。如果你想也計算argv[0],只需將其更改爲從0開始。

最後,正如我之前說的,你需要使用waitpid(2)獲取的終止狀態度日的孩子返回的實際數量。因此,您只能在waitpid(2)返回並確保孩子正常終止後才能打印結果。另外,要獲取實際的退出代碼,您需要使用宏(如果WIFEXITED返回true,則只能使用該宏)。

所以這裏是所有這些問題的完整方案解決:

// Characters from command line arguments are sent to child process 
// from parent process one at a time through pipe. 
// 
// Child process counts number of characters sent through pipe. 
// 
// Child process returns number of characters counted to parent process. 
// 
// Parent process prints number of characters counted by child process. 

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h>   // for strlen() 
#include <unistd.h>   // for fork() 
#include <sys/types.h>  // for pid_t 
#include <sys/wait.h>  // for waitpid() 

int main(int argc, char **argv) 
{ 
    int fd[2]; 
    pid_t pid; 

    if (pipe(fd) < 0) { 
     perror("pipe(2) error"); 
     exit(EXIT_FAILURE); 
    } 

    pid = fork(); 

    if (pid < 0) { 
     perror("fork(2) error"); 
     exit(EXIT_FAILURE); 
     } 

    if (pid == 0) { 

     int readIn; 
     int charCounter = 0; 
     char readbuffer[80]; 

     if (close(fd[1]) < 0) { 
      perror("close(2) failed on pipe's write channel"); 
      /* We use abort() here so that the child terminates with SIGABRT 
      * and the parent knows that the exit code is not meaningful 
      */ 
      abort(); 
     } 

     readIn = read(fd[0], readbuffer, sizeof(readbuffer)); 
     while (readIn > 0) { 
      int i; 
      for (i = 0; i < readIn; i++) { 
       if (readbuffer[i] != ' ') { 
        charCounter++; 
       } 
      } 
      readIn = read(fd[0], readbuffer, sizeof(readbuffer)); 
     } 

     if (readIn < 0) { 
      perror("read(2) error"); 
     } 

     printf("The value of charCounter is %d\n", charCounter); 

     return charCounter; 
     } else { 
     int status; 

     if (close(fd[0]) < 0) { 
      perror("close(2) failed on pipe's read channel"); 
      exit(EXIT_FAILURE); 
     } 

     int i; 
     for (i = 1; i < argc; i++) { 
      size_t to_write = strlen(argv[i]); 
      ssize_t written = write(fd[1], argv[i], to_write); 
      if (written != to_write) { 
       if (written < 0) { 
        perror("write(2) error"); 
       } else { 
        fprintf(stderr, "Short write detected on argv[%d]: %zd/%zd\n", i, written, to_write); 
       } 
      } 
     } 

     if (close(fd[1]) < 0) { 
      perror("close(2) failed on pipe's write channel on parent"); 
      exit(EXIT_FAILURE); 
     } 

     if (waitpid(pid, &status, 0) < 0) { 
      perror("waitpid(2) error"); 
      exit(EXIT_FAILURE); 
     } 

     if (WIFEXITED(status)) { 
      printf("CS201 - Assignment 3 - Andy Grill\n"); 
      printf("The child processed %d characters\n\n", WEXITSTATUS(status)); 
     } else if (WIFSIGNALED(status)) { 
      fprintf(stderr, "Child terminated abnormally with signal %d\n", WTERMSIG(status)); 
     } else { 
      fprintf(stderr, "Unknown child termination status\n"); 
     } 

     return 0; 
    } 
} 

最後的一些注意事項:

  • 外殼由空格分割的參數,所以如果你啓動的程序爲./a.out this is a test,該代碼不會看到單個空間。這是無關緊要的,因爲無論如何都應該忽略空格,但如果要測試代碼真的忽略空格,則需要引用這些參數,以便shell不處理它們,如./a.out "this is a test" "hello world" "lalala"
  • 只使用程序退出代碼的最右邊(最低有效)8位,因此WEXITSTATUS永遠不會返回超過255個。如果子讀取的字符數超過255個,則該值將環繞,因此您實際上有一個字符計數器模256.如果這是一個問題,那麼你需要採取另一種方法,並設置第二個管道進行孩子到家長的溝通,並在那裏寫出結果(並讓父母讀取它)。您可以在man 2 waitpid證實了這一點:

WEXITSTATUS(狀態)

返回子的退出狀態。這包括狀態參數的最低 顯著8位,在調用中指定的子 退出(3)或_exit(2)或作爲主要參數爲迴歸 聲明()。僅當 WIFEXITED返回true時才應使用此宏。

+0

哦,男人,非常感謝你!在你評論的時候,我設法解決了你在這裏解釋的大部分內容(儘管這確實證明我做的是正確的事情)。真正有幫助的是size_t to_write = strlen(argv [i]); strlen()我從來沒有做過,只能從argv獲得4個字符。非常感謝您的幫助,它有助於完成我的任務! –

+0

@ BlackDahlia1147很高興能幫到你。我希望你在課堂​​上有愉快的學習經歷。 –

相關問題