2011-05-26 100 views
2

我需要計算通過標準輸入發送到子進程的字節數,以及子進程寫入標準輸出和標準錯誤的字節數。子進程調用execvp,所以我無法從進程內部監視這些統計信息。我目前的策略是創建3個額外的子進程,每個子進程通過管道監視每個std流(或者在stdin的情況下,從stdin讀取)。在標準輸入,標準輸出和標準錯誤中的監視器字節C

這種策略看起來真的很虛弱,我做了一些奇怪的事情,使得監視stdout/err的進程無法從管道各自的末端讀取(並使它們無限期掛起)。下面的代碼。

這將創建三個輔助子進程,並應讓他們算統計:

void controles(struct fds *des) 
{ 
    int ex[2]; 
    int err[2]; 

    int n_in = 0; 
    int c_in; 

    int n_ex = 0; 
    int c_ex; 

    int n_err = 0; 
    int c_err; 

    pipe(ex); 
    pipe(err); 

    /*has two fields, for the write end of the stdout pipe and the stderr pipe. */ 
    des->err = err[1]; 
    des->ex = ex[1]; 
    switch (fork()) { 
    case 0:  /*stdin */ 
     while (read(0, &c_in, 1) == 1) 
      n_in++; 
     if (n_in > 0) 
      printf("%d bytes to stdin\n", n_in); 
     exit(n_in); 
    default: 
     break; 
    } 

    switch (fork()) { 
    case 0:  /*stdout */ 
     close(ex[1]); 
     /*pretty sure this is wrong */ 
     while (read(ex[0], &c_ex, 1) == 1) { 
      n_ex++; 
      write(1, &c_ex, 1); 
     } 
     if (n_ex > 0) 
      printf("%d bytes to stdout\n", n_ex); 
     close(ex[0]); 
     exit(n_ex); 
    default: 
     close(ex[0]); 
    } 
    switch (fork()) { 
    case 0:  /*error */ 
     close(err[1]); 
     /*also probably have a problem here */ 
     while (read(err[0], &c_err, 1) == 1) { 
      n_err++; 
      write(2, &c_err, 1); 
     } 
     if (n_err > 0) 
      printf("%d bytes to stderr\n", n_err); 
     close(err[0]); 
     exit(n_err); 
    default: 
     close(err[0]); 
    } 
} 

,這是一個代碼片段(子進程中),其從FDS結構設置兩個FD的以便子進程應該寫入管道而不是stdin/stderr。

dup2(des.ex, 1); 
dup2(des.err, 2); 
close(des.ex); close(des.err); /*Is this right?*/ 
execvp(opts->exec, opts->options); /*sure this is working fine*/ 

我迷路了,任何幫助,將不勝感激。

+0

1.你可以在一個進程中提供所有三個管道(根據你希望結果如何傳遞,它甚至可以是一個shell腳本)2.如果進程掛起,這可能在工作中緩衝;確保管道沒有緩衝(至少對於輸入管道) – sehe 2011-05-26 21:38:38

回答

4

我認爲你的代碼可以通過打破一些事情來改善;會計和複製程序基本上都是相同的任務,如果你選擇繼續來與多個進程的道路,可以簡單地寫:

void handle_fd_pair(char *name, int in, int out) { 
    char buf[1024]; 
    int count = 0, n; 
    char fn[PATH_MAX]; 
    snprintf(fn, PATH_MAX - 1, "/tmp/%s_count", name); 
    fn[PATH_MAX-1] = '\0'; 
    FILE *output = fopen(fn, "w"); 
    /* handle error */ 

    while((n = read(in, buf, 1024)) > 0) { 
     count+=n; 
     writen(out, buf, n); /* see below */ 
    } 

    fprintf(output, "%s copied %d bytes\n", name, count); 
    fclose(output); 
} 

而不是一個字符-AT-A-時間,這是低效的溫和的數據,我們可以處理從Advanced Programming in the Unix Environment源代碼與writen()功能部分寫入:

ssize_t    /* Write "n" bytes to a descriptor */ 
writen(int fd, const void *ptr, size_t n) 
{ 
    size_t  nleft; 
    ssize_t  nwritten; 

    nleft = n; 
    while (nleft > 0) { 
     if ((nwritten = write(fd, ptr, nleft)) < 0) { 
      if (nleft == n) 
       return(-1); /* error, return -1 */ 
      else 
       break;  /* error, return amount written so far */ 
     } else if (nwritten == 0) { 
      break; 
     } 
     nleft -= nwritten; 
     ptr += nwritten; 
    } 
    return(n - nleft);  /* return >= 0 */ 
} 

有了幫手,我覺得其餘的可以去更容易。爲每個流分配一個 新孩子,並將in[0]讀端,out[1]err[1]管道的寫入端給孩子。

所有每個孩子的close()電話是很醜陋的,但試圖 寫身邊所有的FDS陣列的小包裝,並剔除該 那些作爲參數傳入,也好像麻煩。

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

#ifndef PATH_MAX 
#define PATH_MAX 128 
#endif 

void handle_fd_pair(char *name, int in, int out) { 
    char buf[1024]; 
    int count = 0, n; 
    char fn[PATH_MAX]; 
    snprintf(fn, PATH_MAX - 1, "/tmp/%s_count", name); 
    fn[PATH_MAX-1] = '\0'; 
    FILE *output = fopen(fn, "w"); 
    /* handle error */ 

    while((n = read(in, buf, 1024)) > 0) { 
     count+=n; 
     writen(out, buf, n); /* see below */ 
    } 

    fprintf(output, "%s copied %d bytes\n", name, count); 
    fclose(output); 
} 

int main(int argc, char* argv[]) { 
    int in[2], out[2], err[2]; 
    pid_t c1, c2, c3; 

    pipe(in); 
    pipe(out); 
    pipe(err); 

    if ((c1 = fork()) < 0) { 
     perror("can't fork first child"); 
     exit(1); 
    } else if (c1 == 0) { 
     close(in[0]); 
     close(out[0]); 
     close(out[1]); 
     close(err[0]); 
     close(err[1]); 
     handle_fd_pair("stdin", 0, in[1]); 
     exit(0); 
    } 

    if ((c2 = fork()) < 0) { 
     perror("can't fork second child"); 
     exit(1); 
    } else if (c2 == 0) { 
     close(in[0]); 
     close(in[1]); 
     close(out[1]); 
     close(err[0]); 
     close(err[1]); 
     handle_fd_pair("stdout", out[0], 1); 
     exit(0); 
    } 

    if ((c3 = fork()) < 0) { 
     perror("can't fork third child"); 
     exit(1); 
    } else if (c3 == 0) { 
     close(in[0]); 
     close(in[1]); 
     close(out[0]); 
     close(out[1]); 
     close(err[1]); 
     handle_fd_pair("stderr", err[0], 2); 
     exit(0); 
    } 

    /* parent falls through to here, no children */ 

    close(in[1]); 
    close(out[0]); 
    close(err[0]); 
    close(0); 
    close(1); 
    close(2); 
    dup2(in[0], 0); 
    dup2(out[1], 1); 
    dup2(err[1], 2); 
    system(argv[1]); 
    exit(1); /* can't reach */ 
} 

好像玩具應用程序的工作呢:)

$ ./dup cat 
hello 
hello 
$ ls -l *count 
-rw-r--r-- 1 sarnold sarnold 22 2011-05-26 17:41 stderr_count 
-rw-r--r-- 1 sarnold sarnold 21 2011-05-26 17:41 stdin_count 
-rw-r--r-- 1 sarnold sarnold 22 2011-05-26 17:41 stdout_count 
$ cat *count 
stderr copied 0 bytes 
stdin copied 6 bytes 
stdout copied 6 bytes 

我認爲這是值得指出的是,你可以實現這個 的程序,只有一個過程和使用select(2)來確定哪些文件描述符需要讀取和寫入。

+0

謝謝。我的主要錯誤是我沒有正確關閉所有文件描述符(導致子進程掛起),但是您幫助我組織並更好地理解了我的代碼。 – bejar37 2011-05-27 14:44:09

0

總的來說,我認爲你是在正確的軌道上。

一個問題是,在stderr和stdout處理程序中,應該從管道中選取字節並寫入真實的stderr/stdout,然後寫回同一個管道。

這也將有助於看到你如何開始子進程。你給了一個代碼片段來關閉真正的stderr,然後將管道fd重複回到stderr的fd,但是你可能想要在父進程中(在fork和before exec之後)這樣做,這樣你就不需要修改源代碼孩子的過程。你應該能夠一般地從父母那裏做到這一點。

+0

嗯,我應該更清楚。我遇到的問題是,在處理程序中,無限期地讀取塊的調用,我不知道爲什麼。 我不應該這樣做,因爲我只想監視子進程寫入stdout/err的內容,而不是父進程。 – bejar37 2011-05-26 21:44:44

相關問題