Linux – Interprocess Communication (Pipes)

Directory

1. Inter-process communication

Second, the pipeline

anonymous pipe

named pipe


One, inter-process communication

InterProcess Communication (IPC, InterProcess Communication), that is, the dissemination or exchange of information between different processes; since the general process user address space is independent and cannot directly access the address space of other processes, information exchange between processes must pass through the system Kernel conduct;

Inter-process communication purpose

  • Data transfer, sending data from one process to another process;
  • Resource sharing, sharing the same resources between multiple processes;
  • Notification event, a process sends information to another process or a group of processes to notify them that a certain event has occurred (such as notifying the parent process when the process terminates);
  • Process control. Some processes want to completely control the execution of another process (such as debugging). At this time, the process hopes to be able to intercept all exceptions of another process and be able to know its status changes in a timely manner;

Classification of inter-process communication

  • pipe
    • anonymous pipe
    • named pipe
  • System V IPC
    • system V message queue
    • system V shared memory
    • system V semaphore
  • POSIX IPC
    • message queue
    • Shared memory
    • amount of information
    • mutex
    • Moderator
    • read-write lock

two, pipeline

Anonymous pipe pipe, named pipe;

Anonymous Pipeline

Linux connects multiple commands by using a vertical bar (pipe character | ) to form a pipeline; the output of the command before the pipe character is used as the input of the command after the pipe character, and the data in the pipe can only flow in one direction (that is, half-duplex communication ), two pipes need to be created to achieve bidirectional flow; in addition, this pipe is an anonymous pipe, which will be automatically destroyed when used up, and can only communicate between parent and child processes;

[wz@192 Desktop]$ cat test.c | grep main
int main() 

The parent process needs to open the file for reading and writing, so that the child process can only read and write when inheriting, and then close the corresponding reading and writing of the parent and child processes to realize the transmission of information; communication can also be done without closing the corresponding reading and writing, but it is generally closed prevent misuse;

pipeline function pipe

  • Create an anonymous pipe
    • Created successfully, return 0;
    • Create failed, return -1;

pipefd is an array of file descriptors

  • pipefd[0], specifies the pipe read end, the default value is 3;
  • pipefd[1], specifies the write end of the pipe, the default value is 4;
#include <stdio.h>
#include <unistd.h>
    
int main()
{
  int pipefd[2];
  if(pipe(pipefd) < 0)
  {
    perror("pipe");
    return 1;
  }
  printf("pipefd[0]: %d\\
", pipefd[0]);
  printf("pipefd[1]: %d\\
", pipefd[1]);
  return 0;
} 
[wz@192 pipe]$ ./test
pipefd[0]: 3
pipefd[1]: 4
int main()
{
  int pipefd[2];
  if(pipe(pipefd) < 0){
    perror("pipe");
    return 1;
  }
  char buf[32];
  write(pipefd[1],"hellopipe",32); //Write into the pipe
  read(pipefd[0],buf,32); //read from the pipe
  printf("buf: %s\\
", buf);
  return 0;
} 
[wz@192 pipe]$ ./test
buf: hellopipe

//The child process writes and the parent process reads
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
  
int main()
{
    int pipefd[2];
    if(pipe(pipefd) < 0){
        perror("pipe");
        return 1;
    }
  
    pid_t id = fork();
    if(id < 0){
        perror("fork");
        return 1;
    }
  
    else if(id == 0){
        close(pipefd[0]);
        char* msg = "child msg";
        int count=5;
        while(count){
            printf("child write: %s\\
",msg);
            write(pipefd[1],msg,strlen(msg));
            sleep(1);
            count--;
       }
       close(pipefd[1]);
       exit(0);
    }
    
    else{
        char buf[64];
        close(pipefd[1]);
        while(1){
            ssize_t sz=read(pipefd[0],buf,sizeof(buf)-1);
            if(sz>0){
                buf[sz]=0;
                printf("father read: %s\\
",buf);
            }
            else if(sz==0){
                printf("pipe file empty!\\
");
                break;
            }
        }
        close(pipefd[0]);
        printf("close read\\
");

        int status = 0;
        pid_t wait_pid = waitpid(id, & status,0);
        if(WIFEXITED(status) & amp; & amp; wait_pid==id)
            printf("child exit normal, exit code: %d\\
", WEXITSTATUS(status));
        else
            printf("child exit error, exit sig: %d\\
", WTERMSIG(status));
    }
    return 0;
}
  • If the pipe is empty, the reader needs to wait for the data to be ready, that is, read blocks;
  • If the pipe is full on the write end, you need to wait for the pipe to have free space before continuing to write, that is, write blocking;
  • The pipeline has its own synchronization mechanism;
  • Pipes are one-way communication;
  • Pipelines are byte stream oriented;
  • Pipes can only ensure communication between processes that are related by blood;
  • Pipes can ensure a certain degree of atomicity in data reading;
//The child process continues to write, and the parent process closes reading
// At this time, the OS will directly close the child process
    else{
        char buf[64];
        close(pipefd[1]);
        while(1){
            ssize_t sz=read(pipefd[0],buf,sizeof(buf)-1);
            if(sz>0){
                buf[sz]=0;
                printf("father read: %s\\
",buf);
                close(pipefd[0]);
                break;
            }
            else if(sz==0){
                printf("pipe file empty!\\
");
                break;
            }
        }
        printf("close read\\
");

        int status = 0;
        pid_t wait_pid = waitpid(id, & status,0);
        if(WIFEXITED(status) & amp; & amp; wait_pid==id)
            printf("child exit normal, exit code: %d\\
", WEXITSTATUS(status));
        else
            printf("child exit error, exit sig: %d\\
", WTERMSIG(status));
    }
//The exit signal of the child process is 13, that is, SIGPIPE
[wz@192 pipe]$ ./test
child write: child msg
father read: child msg
close read
child write: child msg
child exit error, exit sig: 13
[wz@192 pipe]$ ulimit -a
core file size (blocks, -c) 0
data segment size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7154
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

Named Pipe

Named pipes are a special type of file that can exchange data between unrelated processes and are implemented using FIFO files;

Use the command mkfifo to create a named pipe

[wz@192 pipe]$ mkfifo pipefile
[wz@192 pipe]$ ll pipefile
prw-rw-r--. 1 wz wz 0 August 18 08:24 pipefile

Use the function mkfifo to create a named pipe

The difference between anonymous pipes and named pipes

  • Anonymous pipes are created and opened by the function pipe;
  • The named pipe is created by the function or command mkfifo, and then opened by open;
  • The only difference is the creation and opening methods;
[wz@192 pipe]$ echo abc > pipefile 
[wz@192 pipe]$ while:; do echo "1,##########"; cat pipefile; echo "2,#########\ "; sleep 1; done
1,##########
abc
2,#########
1,##########

Implement server & client communication

//makefile
.PHONY:all
all: server client
    
server:server.c
  gcc -o $@ $^
client:client.c
  gcc -o $@ $^
    
.PHONY:clean
clean:
  rm -rf server client 
//server.c
//Create a named pipe and read
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
    
int main()
{
  int ret = mkfifo("pipefile", 0644);
  if(ret == -1){
    perror("mkfifo");
    return 1;
  }
  int pipefd = open("pipefile", O_RDONLY);
  if(pipefd < 0){
    perror("open");
    return 2;
  }
  char msg[64]={0};
  while (1) {
    printf("please wait ...\\
");
    ssize_t sz = read(pipefd, msg, sizeof(msg)-1);
    if(sz > 0){
      msg[sz]=0;
      printf("server read: %s\\
", msg);
    }
    else if(sz == 0){
      printf("client quit!\\
");
      break;
    }
    else {
      perror("read");
      return 3;
    }
  }
  close(pipefd);
  return 0;
} 
//client.c
//Write to the pipe
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
    
int main()
{
  int pipefd = open("pipefile", O_WRONLY);
  if(pipefd < 0){
    perror("open");
    return 1;
  }
    
  char msg[64]={0};
  while(1){
    printf("please write ...\\
");
    ssize_t sz = read(0, msg, sizeof(msg)-1);
    if(sz>0){
      msg[sz]=0;
      write(pipefd, msg, strlen(msg));
    }
    else if(sz==0){
      printf("client read empty!\\
");
      break;
    }
    else{
      perror("client read\\
");
      return 2;
    }
  }
  close(pipefd);
  return 0;
}