Thursday, November 13, 2014

Unix Prog: IPC Coprocesses

1. Coprocesses:

A coprocess normally runs in the background from a shell, and its standard input and standard output are connected to another program using a pipe. Following diagram can illustrate this:

2. Coprocess Example
Here we implement a very simple coprocess add2:

add2.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
 #include<string.h>  
   
 #define MAXLINE 20  
   
 int main(void)  
 {  
  int n, int1, int2;  
  char line[MAXLINE];  
   
  while((n = read(STDIN_FILENO, line, MAXLINE)) > 0) {  
   line[n] = 0;  
   if(sscanf(line, "%d%d", &int1, &int2) == 2) {  
    memset(line, 0, MAXLINE);  
    sprintf(line, "%d\n", int1 + int2);  
    n = strlen(line);  
    if(write(STDOUT_FILENO, line, n) != n) {  
     printf("write error!\n");  
     exit(1);  
    }  
   } else {  
    if(write(STDOUT_FILENO, "invalid args\n", 13) != 13) {  
     printf("write error!\n");  
     exit(2);  
    }  
   }  
  }  
   
  exit(0);  
 }  

main.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<signal.h>  
 #include<unistd.h>  
 #include<string.h>  
   
 #define MAXLINE 20  
   
 void sig_pipe(int signo)  
 {  
  printf("SIGPIPE caught\n");  
  exit(1);  
 }  
   
 int main(int argc, char* argv[])  
 {  
  int n, fd1[2], fd2[2];  
  pid_t pid;  
  char line[MAXLINE];  
   
  // Setup the signal for SIGPIPE  
  if(signal(SIGPIPE, sig_pipe) == SIG_ERR) {  
   printf("signal error!\n");  
   exit(1);  
  }  
   
  // Setup the pipe file descriptors  
  if(pipe(fd1) < 0 || pipe(fd2) < 0) {  
   printf("pipe error!\n");  
   exit(2);  
  }  
   
  // Create the child process  
  if((pid = fork()) < 0) {  
   printf("fork error!\n");  
   exit(3);  
  } else if (pid > 0) { // Parent process  
   close(fd1[0]);  
   close(fd2[1]);  
   
   while(fgets(line, MAXLINE, stdin) != NULL) {  
    n = strlen(line);  
    if(write(fd1[1], line, n) != n) {  
     printf("write error to pipe\n");  
     exit(4);  
    }  
    if((n = read(fd2[0], line, MAXLINE)) < 0) {  
     printf("read error from pipe\n");  
     exit(5);  
    }  
    if(n == 0) {  
     printf("child closed pipe");  
     break;  
    }  
   
    // output the result  
    line[n] == 0;  
    if(fputs(line, stdout) == EOF) {  
     printf("fputs error!\n");  
     exit(6);  
    }  
   }  
   
   if(ferror(stdin)) {  
    printf("fgets error on stdin");  
    exit(6);  
   }  
   
   exit(0);  
  } else { // child process  
   close(fd1[1]);  
   close(fd2[0]);  
   if(fd1[0] != STDIN_FILENO) {  
    if(dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) {  
     printf("dup2 error to stdin\n");  
     exit(1);  
    }  
    close(fd1[0]);  
   }  
   
   if(fd2[1] != STDOUT_FILENO) {  
    if(dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) {  
     printf("dup2 error to stdout\n");  
     exit(1);  
    }  
    close(fd2[1]);  
   }  
   
   if(execl("./add2.out", "add2.out", (char*)0) < 0) {  
    printf("execl error!\n");  
    exit(2);  
   }  
  }  
   
  exit(0);  
 }  

shell:
The process just establish two pipes with child process, who execute the add2.out. Parent process input string to child process, child process read the input and add numbers and put result into output, which then go back to the parent process.

Note: we can't use standard I/O at child process, since if detecting that child process's output is a pipe, child process will make I/O full buffer, and then we can not see the output immediately.
But we can choose to setup the buffer option of standard I/O with setvbuf
 ubuntu@ip-172-31-23-227:~$ ./main.out  
 90 100  
 190  

No comments:

Post a Comment