zoukankan      html  css  js  c++  java
  • 解决进程间标准输入/输出重定向的问题

    这里先看两个例子:
     
    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!
    ");
    22 
    23         printf ("this is stdout string!
    ");
    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
    ", errno, strerror(errno));
    48 
    49         }
    50 
    51  
    52 
    53         exit(0);
    54 
    55 }

    运行:

    :这里printf ("this is stdout string! ");为什么会在文件中打印两句出来?而在不重定向的情况下只打印一次;
     
    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!
    ");
     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!
    ");
     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] = '';
     62 
     63                         printf ("%s
    ", 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!
    ");
     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
    ", 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。
  • 相关阅读:
    Java实现 LeetCode 148 排序链表
    Java实现 LeetCode 148 排序链表
    Java实现 LeetCode 148 排序链表
    Java实现 LeetCode 147 对链表进行插入排序
    Java实现 LeetCode 147 对链表进行插入排序
    VC++的菜单控制和自绘菜单
    仅通过崩溃地址找出源代码的出错行
    积累的VC编程小技巧之框架窗口及其他
    积累的VC编程小技巧之图标、光标及位图
    积累的VC编程小技巧之视图
  • 原文地址:https://www.cnblogs.com/water-moon/p/5995705.html
Copyright © 2011-2022 走看看