2013-10-11 120 views
1

我正在嘗試編寫以下2部分程序。在一個文件(「root.c」)中,我用一個1和0的隨機字符串讀取。然後我將結果字符串分成兩半,並通過fork()將每個半部分發送到自己的進程中。每個子進程使用execl()來運行第二個程序(「bit_count.c」)。C - 使用管道,選擇,分叉和execl創建進程樹

在bit_count.c中,它: a)檢查(半)串的長度是否等於或小於2。如果是,則返回
1和0的數字到它的父進程。 b)如果沒有,它開始遞歸地將字符串分成兩半,並將每一半發送到它自己的新進程(在root.c中複製該過程)。這會創建一個二進制進程樹,直到所有的字符串都是2個字符或更少。 c)左邊和右邊兒童的計數結果由家長彙總,並返回給其父母,直到返回聚合最高兩個子女的根進程,並將其輸出給用戶。

我與這個項目的問題是將2個字符的計數返回給父項。我現在的想法是將父母的左右讀取管道用dup2()指向標準輸入,並且只是用孩子的fprintf打印到stdout。父級的select()函數應該捕獲返回的輸出,對吧?

我的第二個問題是輸出的格式。如果計數是ints,在這種情況下使用select()返回的最好方法是什麼?我附上了我的代碼,只是被警告說它可能是一團糟 - 我用C代碼生鏽了,這是我第一次接觸select()和execl()。

root.c:

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

int main(int argc, char* argv[]) { 
if (argc != 2) { 
    perror("input file name"); 
    printf("%d", argc); 
    exit(1); 
} 

FILE* fp; 
if((fp = fopen(argv[1], "r")) == NULL) { 
    perror("open file"); 
} 
fseek(fp, 0, SEEK_END); 
long fsize = ftell(fp); 
fseek(fp, 0, SEEK_SET); 
char *bits = malloc(fsize+1); 
fread(bits, fsize, 1, fp); 
fclose(fp); 


char *left_half = malloc(fsize/2 + 1); 
char *right_half; 
if (fsize%2) right_half = malloc(fsize/2 + 2); 
else right_half = malloc(fsize/2 + 1); 

if (!left_half || !right_half) perror("array split"); 
memcpy(left_half, bits, fsize/2); 
if (fsize%2) memcpy(right_half, bits + fsize/2, fsize/2 + 1); 
else memcpy(right_half, bits + fsize/2, fsize/2); 


int fd_left[2], fd_right[2]; 
int zero, one; 
int *left_res, *right_res; 
pid_t left, right; 
struct timeval tv; 
fd_set readfds; 

tv.tv_sec = 2; 
tv.tv_usec = 500000; 

if ((pipe(fd_left) == -1) || (pipe(fd_right) == -1)){ 
     perror("Create pipe error"); 
     exit(1); 
} 

FD_ZERO(&readfds); 
FD_SET(fd_left[0], &readfds); 
FD_SET(fd_right[0], &readfds); 

if ((left=fork()) == 0) { 
     close(fd_left[0]); 
     execl("./bit_count", "bit_count", left_half, NULL); 
     perror("initiating recursion"); 
     exit(1); 
} 
else if(left > 0) { 
    if ((right = fork())==0) { 
     close(fd_right[0]); 
     execl("./bit_count", "bit_count", right_half, NULL); 
     perror("initiating recursion"); 
     exit(1); 
    } 
    else if (right > 0) { 
     close(fd_right[1]); 
     close(fd_left[1]); 
     char *left; 
     char *right; 
     dup2(fd_left[0], 0); 
     dup2(fd_right[0], 0); 

     int ret = select(2, &readfds, NULL, NULL, &tv); 
     read(fd_left[0], &left_res, 1); 
     read(fd_right[0], &right_res, 1);   
     printf("Back in root process!\n"); 

    } 
} 

zero = (*right_res + *left_res); 
one = (*(left_res+sizeof(int)) + *(right_res+sizeof(int))); 

printf("%s had %d zeroes and %d ones\n", argv[1], zero, one); 
return 0; 
} 

bit_count.c(僅相關部分):

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

int main(int argc, char* argv[]) { 
if (argc != 2) { 
    perror("sent bit string"); 
    printf("%d", argc); 
    exit(1); 
} 
char *bit_string = argv[1]; 
int size = strlen(bit_string); 
int counts[2]; 
counts[0] = 0; 
counts[1] = 0; 
if (!(size > 2)) { 

    int i=0; 
    for(; i < size; i++) { 
     if (bit_string[i]=='1') ++counts[1]; 
     else ++counts[0]; 
    } 

    fprintf(stdout, "%p", &counts); 
    fflush(stdout); 
    return 0; 
} 
    } 

回答

1
  1. 我的想法,現在是直接父的左,右讀管道stdin與dup2(),只是打印到stdout與fprintf從孩子。父級的select()函數應該捕獲返回的輸出,對吧?

號你需要DUP2(FD [1],STDOUT_FILENO)在孩子調用execl的()之前。 bit_count應該怎麼知道管道?然後在家長,你可以從fd [0]讀取。爲了使事情變得更簡單,你可以使bit_count成爲一個函數,並直接在子中調用它,而不使用execl()。然後,您可以只寫入fd [1](如果您將其設置爲全局,或將值傳遞給bit_count函數)。

  1. 我的第二個問題是輸出的格式。如果計數是ints,在這種情況下使用select()返回的最好方法是什麼?

你可以使用write(STDOUT_FILENO, &counts, 2*sizeof(int))直接寫整數到管道,而不是格式化它們作爲一個字符串。這樣父母不需要將它們轉換回整數。

+0

感謝您的回覆!我將這行添加到了每個分叉的子級,並且在bit_count的末尾添加了write()調用。雖然輸出現在至少說我收到8個字節,但我仍然收到不可能的數字(例如,01和01,我得到每個孩子0和5的計數,而不是1和1。我知道bit_count計數正確,接收它仍然有問題。我應該從父母的閱讀管道中刪除dup2(他們在我的原始代碼中)?另外,不幸的是execl需要使用。謝謝你的幫助! – user2865485

+0

你還在讀這樣的:'read(fd_left [0],&left_res,1);'?一對夫婦的問題:left_res被聲明爲一個指向int的指針,所以當你執行&left_res時,你會得到指針指向int的指針。你最好聲明'int left_res;',或者可能'int left_res [2];'。另外,你告訴'read'只讀1個字節。如果每個孩子正在寫兩個整數,你還需要在父母中讀取這個數額。 –

+0

太棒了,謝謝!這個伎倆! – user2865485