Inter-process communication - pipe communication

Table of contents

 1 Pipeline concept

 2 The unnamed pipe (pipe) can only communicate with related processes

step

Precautions 

3 The well-known pipe (fifo) can communicate with any stand-alone process

step

Precautions


 

1 Pipeline concept

Pipes are the oldest form of IPC on UNIX systems, and all UNIX systems provide this communication mechanism. Pipes have
two limitations:
1. Historically, they have been half-duplex (ie, data can only flow in one direction). Some systems now provide full-duplex pipes, but for best portability we should never presuppose that the system uses this feature.
Two, they can only be used between processes with a common ancestor. Usually, a pipeline is created by a process, and then the
process calls fork, after which the pipeline can be used between the parent and child processes. .

Despite these two limitations, half-duplex pipes are the most commonly used form of IPC.

The characteristics of the pipeline:
    1. The pipeline is a half-duplex working mode
    . 2. All the pipelines are special files that do not support positioning operations.
    3. The pipeline is a special file, and the file IO is used for reading and writing. (open, read, write, close)

 2 The unnamed pipe (pipe) can only communicate with related processes

Unnamed pipe: Size 64k
Function interface :
int pipe(int pipefd[2]);
Function :
Create an unnamed pipe for communication (in the kernel)
Parameters :
pipefd: store the first address of the file descriptor array space
pipefd[0]: Read pipe file descriptor
pipefd[1]: write pipe file descriptor
return value :
return 0 if successful 
, return -1 if failed 

step

Create a pipeline == "read and write pipeline ==" close the pipeline 

Precautions 

There is at least one write end in the pipeline :
1. If there is data in the pipeline, read directly 
2. If there is no data in the pipeline, block and wait until there is data written, and then read the data.
There is no write end in the pipeline :
1. If the pipeline If there is data in the pipeline, read directly
2. If there is no data in the pipeline, do not block and wait, and directly return to
at least one read end in the pipeline :
1. Write data to the pipeline, if it is not full, write directly
2. If Write full (64k), block and wait for data to be read before continuing to write to
the pipeline. There is no read end :
1. Writing data to the pipeline will cause a pipeline rupture error

 Let me analyze this unnamed pipe through a code example

                The parent-child process uses an unnamed pipeline to transfer files. Here I use pipeline transmission to generate and copy a picture src.jpg to a dst.jpg

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc, char const *argv[])
{
     pid_t pid;
     int pipefd[2],ret;
     ret = pipe(pipefd);
     if(ret < 0)
     {
          perror("fail to pipe");
          return -1;
     }
     pid = fork();

     if(pid > 0)//父进程写
     {
          close(pipefd[0]);
          int fd;
          ssize_t nret;
          fd = open("./src.jpg",O_RDONLY);
          char tmpbuf[1024] = {0};
          if(-1 == fd)
          {
               perror("fail open");
               return -1;
          } 
          while (1)
          {
               nret = read(fd,tmpbuf,sizeof(tmpbuf));
               if(0 >= nret)break;
               write(pipefd[1],tmpbuf,nret);
          }
          
          close(pipefd[1]);
          close(fd);
          wait(NULL); 
     }
     else if(0 == pid)//子进程读
     {
          close(pipefd[1]);
          int fd;
          ssize_t nret;
          fd = open("./dst.jpg",O_WRONLY | O_CREAT | O_TRUNC,0664);
          if(-1 == fd)
          {
               perror("fail open");
               return -1;
          }
          char tmp[1024] = {0};
          
          while (1)
          {
               nret = read(pipefd[0],tmp,sizeof(tmp));
               if(0 >= nret)break;
               write(fd,tmp,nret);
          }
          
          close(pipefd[0]);
          close(fd);
          exit(0);
     }
     else
     {    
          perror("fail to fork");
     }
     return 0;
}

It can be seen that this is very similar to the operation of file io, but uses the function of the pipeline to communicate between the two parent and child processes

After the code runs, we can see that the sizes of src and dst are exactly the same as 706103

 Needless to say and the picture is the same

 

 

 

3 The well-known pipe (fifo) can communicate with any stand-alone process

Function interface :
int mkfifo(const char *pathname, mode_t mode);
Function :
Create a named pipe
Parameters :
pathname: path of the named pipe
mode: permission of the named pipe
Return value :
return 0 if successful  ,
return -1 if failed 

step

1. Create: mkfifo()

2. Open the famous pipe open()

3. Pipeline reading and writing: file IO

    读: read(fd-read,buff,sizeof(buff));
    写: write(fd-write,buff,sizeof(buff));

4. Close the pipeline: close(fd);

5. Uninstall the pipeline : remove();

Precautions

A well-known pipe must be read and written at both ends before it can continue to execute downwards. Otherwise, if it is
opened in read-only or write-only mode, it will be blocked (waiting for the other end to join).

When opened with O_RDONLR, the pipe blocks at open
When opened with O_WRONLY, the pipe blocks at open

The blocking is only unblocked when both ends are open at the same time.

However, I use two different .c files to use the well-known pipe to chat on the terminal ( two c files, A_B.c is written by process knowledge, and B_A.c is written by thread knowledge ).

A_B.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
     pid_t pid;
     int ret1 = mkfifo("./A_B", 0664); // 创建有名管道
     int ret2 = mkfifo("./B_A", 0664); // 创建有名管道
     if ((-1 == ret1 || -1 == ret2) && errno != EEXIST)
     {
          perror("fail to mkfifo");
          return -2;
     }
     pid = fork();
     if (pid > 0) // 父进程a->b(A发送)
     {
          char tmpbuf[1024] = {0};
          int fa_b = open("./A_B", O_WRONLY); // 打开管道文件(写)
          while (1)
          {
               fgets(tmpbuf, sizeof(tmpbuf), stdin);
               if (!strcmp("quit\n", tmpbuf))break;
               write(fa_b, tmpbuf, strlen(tmpbuf) + 1);
          }
          close(fa_b);
          remove("A_B");
          return 0;
     }
     else if (0 == pid) // 子进程b->a(A接收)
     {
          char tmpbuf[1024] = {0};
          int fb_a = open("./B_A", O_RDONLY); // 打开管道文件(读)
          while (1)
          {
               int nret = read(fb_a, tmpbuf, sizeof(tmpbuf));
               if (!strcmp("quit\n", tmpbuf) || nret <= 0)break;
               printf("\33[32mB->A\33[0m: %s", tmpbuf);
          }
          close(fb_a);
          remove("./B_A");
          return 0;
     }
     else
     {
          perror("fail to fork");
          return 0;
     }
}

 B_A.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void *A_B(void *arg) // a->b(B接收)
{
     char tmpbuf[1024] = {0};
     int fa_b = open("./A_B", O_RDONLY); // 打开管道文件(读)
     while (1)
     {
          int nret = read(fa_b, tmpbuf, sizeof(tmpbuf));
          if (!strcmp("quit\n", tmpbuf))break;
          printf("\33[33mA->B\33[0m: %s", tmpbuf);
     }
     close(fa_b);
     remove("./A_B");
     exit(0);
}
void *B_A(void *arg) // b->a(B发送)
{
     char tmpbuf[1024] = {0};
     int fb_a = open("./B_A", O_WRONLY); // 打开管道文件(写)
     while (1)
     {
          fgets(tmpbuf, sizeof(tmpbuf), stdin);
          if (!strcmp("quit\n", tmpbuf))break;
          write(fb_a, tmpbuf, strlen(tmpbuf) + 1);
     }
     close(fb_a);
     remove("B_A");
     exit(0);
}
int main(int argc, char const *argv[])
{
     pthread_t tid1, tid2;
     int ret1 = mkfifo("./A_B", 0664); // 创建有名管道
     int ret2 = mkfifo("./B_A", 0664); // 创建有名管道
     if ((-1 == ret1 || -1 == ret2) && errno != EEXIST)
     {
          perror("fail to mkfifo");
          return -2;
     }
     pthread_create(&tid1, NULL, A_B, NULL);
     pthread_create(&tid2, NULL, B_A, NULL);

     pthread_join(tid1, NULL);
     pthread_join(tid2, NULL);
     return 0;
}

In this way, when the two codes run at the same time, two channels will be created for half-duplex sending and receiving communication, as shown in the figure (the missing text is due to the bug that half-width and full-width Chinese are deleted in the terminal).

 

 

Guess you like

Origin blog.csdn.net/m0_58193842/article/details/128554609