协同进程

本文参考博文:协同进程

 

  过滤程序:UNIX系统过滤程序从标准输入读取数据,对其进行适当处理后写到标准输出。几个过滤程序通常在shell管道命令行中线性的连接。当一个程序产生某个过滤程序的输入,同时又读取该过滤程序的输出时,则该过滤程序就成为协同进程。

    Korn Shell提供了协同进程,但是Bournce-again shell 和C Shell并没有提供按协同进程方式将进程连接起来的方法。协同进程在shell的后台运行,其标准输入和标准输出通过管道连接到另外一个程序。

     注意:popen只提供连接到另一个进程的标准输入或者标准输出的一个的单向管道,而对于协同进程,则它有连接到另外一个进程的两个单向管道————一个接到其标准输出、一个连接到其标准输入,经过其处理后,再从标准输出读取数据。所以,利用popen并不能实现协同进程。

CODE:

<code>

/***********************************************************************************/

使用popen:

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

#include <signal.h>

#include <fcntl.h>

#define MAX_LINE        2048

static void sig_pipe(int);

int main(void)

{

    int    n, fd1[2], fd2[2];

    char   line[MAX_LINE];

    pid_t pid;

    if(signal(SIGPIPE, sig_pipe) == SIG_ERR) //如果在写入套接字时,读取端已经关闭,那么此时可以得到一个 SIGPIPE 信号,该信号默认情况下会终止当前进程。
//例如,当服务器关闭时,而客户端还试图向套接字写入数据的时候会产生一个 SIGPIPE 信号,此时会造成客户端的非正常的退出,防止这种情况可以给客户端安装一个 //signal() 函数用来处理 SIGPIPE 信号。

    {

        printf("init signal error: %s\n", strerror(errno));

        exit(1);

    }

    if((pipe(fd1) < 0) || (pipe(fd2) < 0)) 

    {

        printf("create pipe error: %s\n", strerror(errno));

        exit(1);

    }

 

    if ((pid = fork()) < 0) 

    {

        printf("fork error: %s\n", strerror(errno));

        exit(1);

   } 

   else if (pid > 0) 

  {

   close(fd1[0]);

   close(fd2[1]);

     while (fgets(line, MAX_LINE, stdin) != NULL) //fgets():非格式化输入

     {

       n = strlen(line);

      if (write(fd1[1], line, n) != n) 

      {

       printf("write to pipe failed: %s\n", strerror(errno));

        exit(1);

       }

     if ((n = read(fd2[0], line, MAX_LINE)) < 0) 

    {

     printf("read from pipe failed: %s\n", strerror(errno));

     exit(1);

    }

    if (n == 0) 

    {

     printf("child closed pipe\n");

     break;

    }

    line[n] = 0;

    if (fputs(line, stdout) == EOF) //fputs:非格式化输出

    {

     printf("fputs error: %s\n", strerror(errno));

     exit(1);

    }

   }

   if (ferror(stdin)) //ferror:错误处理

   {

    printf("fgets error: %s\n", strerror(errno));

    exit(1);

   }

    } 

    else 

    {

   close(fd1[1]);

   close(fd2[0]);

   if (fd1[0] != STDIN_FILENO) 

   {

    if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) //dup2():与文件描述符表相关的操作

    {

     printf("dup2 error: %s\n", strerror(errno));

     exit(1);

    }

    close(fd1[0]);

   }

  

   if (fd2[1] != STDOUT_FILENO) 

   {

    if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) 

    {

     printf("dup2 error: %s\n", strerror(errno));

     exit(1);

    }

    close(fd2[1]);

   } 

   if (execl("./add2", "add2", (char *)0) < 0) 

   {

    printf("execl error: %s\n", strerror(errno));

    exit(1);

   }

exit(0);

}

static void sig_pipe(int signo)//打印单管道

{

printf("SIG_PIPE caught\n");

exit(1);

}

/***********************************************************************************/

</code>

使用opopen:

<code>

/***********************************************************************************/

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

#include <signal.h>

#include <fcntl.h>

#define   MAX_LINE   2048

static void sig_pipe(int);

int

main(void)

{

int    n, fd[2];

char   line[MAX_LINE];

pid_t pid;

FILE   *fpin, *fpout;

if( signal(SIGPIPE, sig_pipe) == SIG_ERR ) 

{

   printf("init signal error: %s\n", strerror(errno));

   exit(1);

}

if((fpout = popen("./add2", "w")) == NULL) 

{

   printf("popen error: %s\n", strerror(errno));

   exit(1);

}

if((fpin = popen("./add2", "r")) == NULL) 

{

   printf("popen error: %s\n", strerror(errno));

   exit(1);

}

 

while(fgets(line, MAX_LINE, stdin) != NULL) 

{

    n = strlen(line);

   fd[0] = fileno(fpout);

   if (write(fd[0], line, n) != n) 

   {

    printf("write to pipe failed: %s\n", strerror(errno));

    exit(1);

   }

   fd[1] = fileno(fpin);

   if ((n = read(fd[1], line, MAX_LINE)) < 0) 

   {

    printf("read from pipe failed: %s\n", strerror(errno));

    exit(1);

   } 

   else if (n == 0) 

   {

    printf("child closed pipe\n");

    break;

   } 

   else 

   {

    line[n] = 0;

    if (fputs(line, stdout) == EOF) 

    {

     printf("fputs error: %s\n", strerror(errno));

     exit(1);

    }

   }

}

if(ferror(stdin)) 

{

   printf("fgets error: %s\n", strerror(errno));

   exit(1);

}

exit(0);

}

static void

sig_pipe( int signo )

{

printf("SIG_PIPE caught\n");

exit(1);

}

/***********************************************************************************/

</code>

过滤程序:

<code>

/***********************************************************************************/

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>

#define   MAX_LINE   2048

int main(void)

{

int        n, int1, int2;

char        line[MAX_LINE]; 

while ((n = read(STDIN_FILENO, line, MAX_LINE)) > 0) 

{

   if (sscanf(line, "%d%d", &int1, &int2) == 2) //sscanf():格式化输入

   {

    sprintf(line, "%d\n", int1 + int2);

    n = strlen(line);

    if (write(STDOUT_FILENO, line, n) != n) {

     printf("write error: %s", strerror(errno));

     exit(1);

    }

   } 

   else 

   {

    if(write(STDOUT_FILENO, "Invalid args\n", 13) != 13) 

    {

     printf("write error: %s", strerror(errno));

     exit(1);

    }

   }

}

exit(0);

}

/***********************************************************************************/

</code>

     他们的实现原理是完全不一样的。使用popen时,popen其实是实现了以下三个动作:1)创建一个pipe; 2)fork一个子进程,使得子进程运行新的程序;3)将子进程的标准输出或者标准输入重定位到父进程。即,父进程通过管道向子进程写数据或者通过管道从子进程读取数据。而这里的“读”或者“写”是根据调用popen时的第二个参数决定的。

      如果使用两次popen,如上面的程序所示,那么实际上父进程是在与两个子进程通过管道通信,而不是一个!

      而对于协同进程而言,它是父进程通过一个管道从子进程读取数据(子进程的标准输出输出到此管道),同时父进程通过子进程向子进程写数据(子进程的标准输入来自此管道),这里的两个子进程其实是一个进程。

       所以,他们有着本质的区别!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值