解決進程間標准輸入/輸出重定向的問題


這里先看兩個例子:
 
1) 源碼如下:
 
 1 #include <sys/types.h>
 2 
 3 #include <stdio.h>
 4 
 5 #include <stdlib.h>
 6 
 7 #include <unistd.h>
 8 
 9 #include <errno.h>
10 
11  
12 
13 int main(int argc, char *argv[])
14 
15 {
16 
17         pid_t pid;
18 
19  
20 
21         fprintf (stderr, "this is stderr string!\n");
22 
23         printf ("this is stdout string!\n");
24 
25  
26 
27         if ((pid = fork()) == 0)
28 
29         {
30 
31  
32 
33         }
34 
35         else if (pid > 0)
36 
37         {
38 
39  
40 
41         }
42 
43         else
44 
45         {
46 
47                 fprintf (stderr, "fork error! errno: %d errstr: %s\n", errno, strerror(errno));
48 
49         }
50 
51  
52 
53         exit(0);
54 
55 }

運行:

:這里printf ("this is stdout string!\n");為什么會在文件中打印兩句出來?而在不重定向的情況下只打印一次;
 
2) 這是一個進程間的pipe通信,代碼如下:
 
  1 // main_process.c
  2 
  3 #include <sys/types.h>
  4 
  5 #include <stdio.h>
  6 
  7 #include <stdlib.h>
  8 
  9 #include <unistd.h>
 10 
 11  
 12 
 13 int main(int argc, char *argv[])
 14 
 15 {
 16 
 17         int pipe_fd[2];
 18 
 19         pid_t pid;
 20 
 21         char pipe_buf[1024];
 22 
 23         int ret;
 24 
 25  
 26 
 27         if (pipe(pipe_fd) < 0)
 28 
 29                 fprintf(stderr, "pipe failed!\n");
 30 
 31  
 32 
 33         if ((pid = fork()) == 0) /* child */
 34 
 35         {
 36 
 37                 close(pipe_fd[0]);
 38 
 39                 dup2(pipe_fd[1], STDOUT_FILENO);
 40 
 41                 close(pipe_fd[1]);
 42 
 43                 if (execl("./child_process", "./child_process", NULL) == -1)
 44 
 45                         fprintf (stderr, "execl faild!\n");
 46 
 47                 exit(0);
 48 
 49         }
 50 
 51         else if (pid > 0) /* parent */
 52 
 53         {
 54 
 55                 close(pipe_fd[1]);
 56 
 57                 while ((ret = read(pipe_fd[0], pipe_buf, sizeof(pipe_buf) - 1)) > 0)
 58 
 59                 {
 60 
 61                         pipe_buf[ret] = '\0';
 62 
 63                         printf ("%s\n", pipe_buf);
 64 
 65                         fflush(stdout);
 66 
 67                 }
 68 
 69                 close(pipe_fd[0]);
 70 
 71         }
 72 
 73         else
 74 
 75                 fprintf (stderr, "fork failed!\n");
 76 
 77  
 78 
 79         exit(0);
 80 
 81 }
 82 
 83  
 84 
 85 // child_process.c
 86 
 87 #include <sys/types.h>
 88 
 89 #include <stdio.h>
 90 
 91 #include <stdlib.h>
 92 
 93 #include <unistd.h>
 94 
 95  
 96 
 97 int main(int argc, char *argv[])
 98 
 99 {
100 
101         int i;
102 
103  
104 
105         for (i=0; i<100; i++)
106 
107         {
108 
109                 printf ("counter: %d\n", i);
110 
111                 sleep(1);
112 
113         }
114 
115  
116 
117         exit(0);
118 
119 }

我們的原意是將child進程的輸出重定向,然后從main進程中接收並打印出來,根據程序也就是1s打印一次計數,但這個程序在運行中會出現什么問題呢?main進程的read會一直阻塞,直到child進程將pipe_fd[1]寫滿以后才會有返回打印出。

 
其實,這兩個問題都源於一個原因,那就是緩沖的原因。
在輸入輸出的緩沖中,我們知道有三種: 無緩沖、行緩沖和塊緩沖(也叫全緩沖)。
對於標准輸出/標准錯誤輸出,默認的緩沖方式是: 標准錯誤輸出是無緩沖輸出,也就是只要給stderr寫數據,馬上就會送出;標准輸出分兩種情況,如果輸出定向的是到設備終端(tty),那么是行緩沖,如果是其他設備/文件,則是塊緩沖
 
這就可以解釋上面兩種情況了:
1) 但重定向到文件輸出時,printf的內容會在緩沖中,fork時會將當前的緩沖區clone一個,而且內容不變,但進程結束的時候,系統會自動flush緩沖區,所以子進程和服進程均將緩沖區的內容輸出,所以出現了兩次同樣的printf的內容;
2) 主進程將子進程的stdout重定向到pipe輸出,所以child就會塊緩沖,所以並不是每秒去flush一下buffer,而是當buffer滿或者child進程關閉時flush,故main進程的read就不是我們的原意了。
 
弄清楚了原因,解決這個問題也有兩種方法:
1) 在printf后,使用fflush(stdout)手動flush緩沖區;
2) 使用setbuf或setvbuf設置stdout的緩沖方式;
 
小結:
     由於stdout 在定向的位置不一樣會有不一樣的緩沖方式,所以在寫被調用的子進程的時候一定要注意這個問題,最好在使用stdout的地方使用fflush進行手動flush。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM