解决进程间标准输入/输出重定向的问题
这里先看两个例子:
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。