解决进程间标准输入/输出重定向的问题

这里先看两个例子:
 
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。
posted @ 2016-10-25 10:28  water-moon  阅读(3619)  评论(1编辑  收藏  举报