LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 860|回复: 2

请大家来讨论下popen在协同进程方面的怪异行为(我已经实验过了)

[复制链接]
发表于 2007-6-17 06:44:10 | 显示全部楼层 |阅读模式
LInux中pipe只能支持单向通道,而popen实际上就是将(pipe,fork,exec)这三步简化成一步,但是我一开始产生了一个误区,问题在协同进程这,我们都知道,协同进程可以用两个单向管道来构造,我一开始认为用两个popen也可以达到同样的功能,但在理论上这是行不通的,因为,用两个pipe的方法只创建一个子进程(协同进程);而用两次popen会产生两个子进程(两次fork),所以这两种方法的区别应该如下图:

所以说按道理popen的方法是应该不能起到协同进程的作用的,可是结果呢,它同样能很好的运行,下面是两种方法的代码,用过滤程序的功能是从输入得到两个整数,然后输出二者之和:
方法1:pipe

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <signal.h>
  7. #include <fcntl.h>

  8. #define MAX_LINE        2048

  9. static void sig_pipe(int);

  10. int
  11. main(void)
  12. {
  13.         int        n, fd1[2], fd2[2];
  14.         char        line[MAX_LINE];
  15.         pid_t        pid;
  16.        
  17.         if (signal(SIGPIPE, sig_pipe) == SIG_ERR) {
  18.                 printf("init signal error: %s\n", strerror(errno));
  19.                 exit(1);
  20.         }
  21.        
  22.         if ((pipe(fd1) < 0) || (pipe(fd2) < 0)) {
  23.                 printf("create pipe error: %s\n", strerror(errno));
  24.                 exit(1);
  25.         }
  26.        
  27.         if ((pid = fork()) < 0) {
  28.                 printf("fork error: %s\n", strerror(errno));
  29.                 exit(1);
  30.         } else if (pid > 0) {
  31.                 close(fd1[0]);
  32.                 close(fd2[1]);
  33.                 while (fgets(line, MAX_LINE, stdin) != NULL) {
  34.                         n = strlen(line);
  35.                         if (write(fd1[1], line, n) != n) {
  36.                                 printf("write to pipe failed: %s\n", strerror(errno));
  37.                                 exit(1);
  38.                         }
  39.                        
  40.                         if ((n = read(fd2[0], line, MAX_LINE)) < 0) {
  41.                                 printf("read from pipe failed: %s\n", strerror(errno));
  42.                                 exit(1);
  43.                         }
  44.                        
  45.                         if (n == 0) {
  46.                                 printf("child closed pipe\n");
  47.                                 break;
  48.                         }
  49.                        
  50.                         line[n] = 0;
  51.                         if (fputs(line, stdout) == EOF) {
  52.                                 printf("fputs error: %s\n", strerror(errno));
  53.                                 exit(1);
  54.                         }
  55.                 }
  56.                 if (ferror(stdin)) {
  57.                         printf("fgets error: %s\n", strerror(errno));
  58.                         exit(1);
  59.                 }
  60.         } else {
  61.                 close(fd1[1]);
  62.                 close(fd2[0]);
  63.                
  64.                 if (fd1[0] != STDIN_FILENO) {
  65.                         if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) {
  66.                                 printf("dup2 error: %s\n", strerror(errno));
  67.                                 exit(1);
  68.                         }
  69.                         close(fd1[0]);
  70.                 }
  71.                
  72.                 if (fd2[1] != STDOUT_FILENO) {
  73.                         if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) {
  74.                                 printf("dup2 error: %s\n", strerror(errno));
  75.                                 exit(1);
  76.                         }
  77.                         close(fd2[1]);
  78.                 }
  79.                
  80.                 if (execl("./add2", "add2", (char *)0) < 0) {
  81.                         printf("execl error: %s\n", strerror(errno));
  82.                         exit(1);
  83.                 }
  84.         }
  85.        
  86.         exit(0);
  87. }

  88. static void
  89. sig_pipe(int signo)
  90. {
  91.         printf("SIG_PIPE caught\n");
  92.         exit(1);
  93. }
复制代码

方法2:popen

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <signal.h>
  7. #include <fcntl.h>

  8. #define MAX_LINE        2048

  9. static void sig_pipe(int);

  10. int
  11. main(void)
  12. {
  13.         int        n, fd[2];
  14.         char        line[MAX_LINE];
  15.         pid_t        pid;
  16.         FILE        *fpin, *fpout;
  17.        
  18.         if (signal(SIGPIPE, sig_pipe) == SIG_ERR) {
  19.                 printf("init signal error: %s\n", strerror(errno));
  20.                 exit(1);
  21.         }
  22.        
  23.         if ((fpout = popen("./add2", "w")) == NULL) {
  24.                 printf("popen error: %s\n", strerror(errno));
  25.                 exit(1);
  26.         }
  27.        
  28.         if ((fpin = popen("./add2", "r")) == NULL) {
  29.                 printf("popen error: %s\n", strerror(errno));
  30.                 exit(1);
  31.         }
  32.        
  33.         while (fgets(line, MAX_LINE, stdin) != NULL) {
  34.                 n = strlen(line);
  35.                 fd[0] = fileno(fpout);
  36.                 if (write(fd[0], line, n) != n) {
  37.                         printf("write to pipe failed: %s\n", strerror(errno));
  38.                         exit(1);
  39.                 }
  40.                 fd[1] = fileno(fpin);
  41.                 if ((n = read(fd[1], line, MAX_LINE)) < 0) {
  42.                         printf("read from pipe failed: %s\n", strerror(errno));
  43.                         exit(1);
  44.                 } else if (n == 0) {
  45.                         printf("child closed pipe\n");
  46.                         break;
  47.                 } else {
  48.                         line[n] = 0;
  49.                         if (fputs(line, stdout) == EOF) {
  50.                                 printf("fputs error: %s\n", strerror(errno));
  51.                                 exit(1);
  52.                         }
  53.                 }
  54.         }
  55.         if (ferror(stdin)) {
  56.                 printf("fgets error: %s\n", strerror(errno));
  57.                 exit(1);
  58.         }
  59.         exit(0);
  60. }

  61. static void
  62. sig_pipe(int signo)
  63. {
  64.         printf("SIG_PIPE caught\n");
  65.         exit(1);
  66. }
复制代码

过滤程序:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <errno.h>

  6. #define        MAX_LINE        2048

  7. int
  8. main(void)
  9. {
  10.         int        n, int1, int2;
  11.         char        line[MAX_LINE];
  12.        
  13.         while ((n = read(STDIN_FILENO, line, MAX_LINE)) > 0) {
  14.                 if (sscanf(line, "%d%d", &int1, &int2) == 2) {
  15.                         sprintf(line, "%d\n", int1 + int2);
  16.                         n = strlen(line);
  17.                         if (write(STDOUT_FILENO, line, n) != n) {
  18.                                 printf("write error: %s", strerror(errno));
  19.                                 exit(1);
  20.                         }
  21.                 } else {
  22.                         if (write(STDOUT_FILENO, "Invalid args\n", 13) != 13) {
  23.                                 printf("write error: %s", strerror(errno));
  24.                                 exit(1);
  25.                         }
  26.                 }
  27.         }
  28.         exit(0);
  29. }
复制代码

这两种方法都可以运行,效果一样,而且在用popen方法的时候,ps一下会看到确实有两个add2子进程,现在问题出来了,为什么popen这种方法也能正常工作呢,小弟刚开始学unix编程不久,还请各位兄弟多来发表一下自己的看法

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2007-6-18 07:47:51 | 显示全部楼层
Post by wawxdyy

这两种方法都可以运行,效果一样,而且在用popen方法的时候,ps一下会看到确实有两个add2子进程,现在问题出来了,为什么popen这种方法也能正常工作呢


两个add2 进程之间没有联系,它们各自的I/O扰乱了楼主的判断。lz 可以考虑在主程序的输出中加一个标记:


  1. $ diff -u popen.c.orig popen.c                                             
  2. --- popen.c.orig  Mon Jun 18 07:31:16 2007
  3. +++ popen.c     Mon Jun 18 07:35:53 2007
  4. @@ -15,7 +15,7 @@
  5. {
  6.         int     n,  fd[2];
  7.         char    line[MAX_LINE];
  8. -       pid_t   pid;
  9. +       /* pid_t        pid; */
  10.         FILE    *fpin,  *fpout;
  11.         
  12.         if (signal(SIGPIPE,  sig_pipe) == SIG_ERR) {
  13. @@ -49,7 +49,8 @@
  14.                         break;
  15.                 } else {
  16.                         line[n] = 0;
  17. -                       if (fputs(line,  stdout) == EOF) {
  18. +                       char tmp[MAX_LINE + 6] = "OUT-> ";
  19. +                       if (fputs(strncat(tmp, line, n),  stdout) == EOF) {
  20.                                 printf("fputs error: %s\n",  strerror(errno));
  21.                                 exit(1);
  22.                         }
复制代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-6-18 11:55:44 | 显示全部楼层
恩,是Ù样,谢谢阿
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表