這里先看兩個例子:
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。
