[Linux] Inter-process communication - named pipe

named pipe

Anonymous pipes can only be used for inter-process communication. To allow blood-related processes to communicate
and allow unrelated processes to communicate, you need to use named pipes for communication.


Because the file has a file name and must have it, it is called a named pipe


1. See the pipeline documentation

mkfifo function

input man mkfifocommand

Make a FIFOS, representing a named pipe


mkfifo fifoMake a pipe and name it fifo
file type starts with p and is called pipe file


input man 3 mkfifocommand


pathname represents the path. If there is no path, there is only the file name. By default , mode represents the mode of creating permissions in the current path , that is, the permission to create files (666, 664)
returns 0 if it succeeds, and -1 if it fails.


Use of pipeline files

insert image description here
Redirect hello world to the fifo pipeline
, but it seems that it will not be written into
the fifo, which only represents a symbol. Writing messages to the symbol will not be flushed to the disk, but will only write hello world to the pipeline,
but the pipeline file is an in-memory file, so the size will not change


By assigning the SSH channel, create terminal 2.
When the output redirection of terminal 1 is guaranteed to run,
cat reads from the display by default.
Use input redirection in terminal 2 to redirect fifo to the display.

Finally, hello world is displayed in terminal 2.
In fact, the start of output redirection and input redirection are both processes and have nothing to do

2. Named pipe principle

insert image description here
To open the corresponding file, a struct file object will be created in the operating system, and the struct file object has its own buffer.
Since 0 1 2 are occupied respectively, 3 points to the struct file object

If there is an unrelated process that also opens the file on the disk, the operating system will no longer create a struct file object, and will
directly fill in the address of the struct file object in the subscript corresponding to the newly created process
in the struct file object There is a reference count in , which is 1 by default. When a new process is created, the reference count will become 2.
At this time, the two processes point to the same file

The purpose is to allow communication between two processes, so the data should not be flushed to the disk, the
disk file should be changed to memory level, and the disk will not be flushed, so name it as a pipeline file

How to ensure that two unrelated processes see and open the same file?

The uniqueness of the file, expressed by the path,
allows different processes to see the same file through the file path + file name, and open it, that is, they see the same resource

3. Use named pipes to realize server&client communication

In vscode, create server.cc file and client.cc file and makefile respectively

How to use makefile to continuously generate executable programs

If the makefile is created in this way, only the server executable program will be executed.
The server is the first real target file encountered by scanning from top
to bottom. When the makefile is scanned from top to bottom, the first set of dependencies and dependent methods will be executed by default.


In order not to let client and server become target files

In this way, two executable programs can be generated at one time

comm.hpp file

Create a public header file comm.hpp, and create a public path and mode internally
(the implementation code of .cpp ending in hpp is mixed into the .h header file, and the definition and implementation are included in the same file)

#pragma once
#include<iostream>
#include<string>
#define NUM 1024
using namespace std;

const string fifoname="./fifo";//管道名字为当前路径创建的fifo
mode_t mode=0666;//默认权限为0666

In this way, the server file and the client file will call the same file path

server.cc server

1. Create a pipeline file

Create a server.cc file and use the mkfifo function to create a pipeline file


At this time, run the executable program to generate the fifo pipeline file


The permission is changed to 664, but the permission set in comm.hpp is 666.
The mode finally needs to be operated with umask


After manually setting the mask to 0, the problem of permission modification can be solved


After manually deleting the fifo, run again

At this time, the permission is still 666 and has not been modified.

2. Let the read-write end process open the file according to their own needs

Print the content of the file descriptor into the buffer.
There are three cases
. If it returns >0, the reading is successful, but the system does not know that the buffer is a string, but we know it ourselves, so we need to add \0 at the end. If it
returns ==0, indicating that the end of the file has been read, and the reader will only read the end of the file when the write end is closed.
If it returns <0, it means that the reading failed, and an error code is returned

3. Overall code

//服务端
#include<iostream>
using namespace std;
#include<sys/stat.h>
#include<sys/types.h>
#include<cerrno>
#include<cstring>
#include<fcntl.h>
#include<unistd.h>
#include"comm.hpp"//公共路径
int main()
{
    
    
    umask(0);//将当前进程的umask掩码设为0
  //创建管道文件,只需要创建一次
  int n=mkfifo(fifoname.c_str(),mode);
  if(n!=0)//创建失败
  {
    
    
    //失败就返回错误码
   cout<<errno<<":"<<strerror(errno)<<endl;
   return 1;
  }
   cout<<"create fifo success"<<endl;
   
   //2.让服务端直接开启管道文件
   int rfd=open(fifoname.c_str(),O_RDONLY);
   //第二个参数代表读
   //以读方式打开文件
   if(rfd<0)//创建失败
   {
    
    
     //失败就返回错误码
     cout<<errno<<":"<<strerror(errno)<<endl;
     return 2;
   }
   cout<<"open fifo success,begin"<<endl;

   // 3.正常通信
   char buffer[NUM];
   while(true)
   {
    
    
      buffer[0]=0;
    //rfd作为文件描述符(0/1/2)
      ssize_t n=read(rfd,buffer,sizeof(buffer)-1);//将rfd的内容读到buffer中
      if(n>0)//读取成功
      {
    
    
       buffer[n]='\0';
       cout<<"client#"<<buffer<<endl;
      }
      else if(n==0)//读到文件结尾为0
      {
    
    
          //写端关闭
          cout<<    "cilent quit,me too"<<endl;
          break;
      }
      else //读取失败
      {
    
    
         cout<<errno<<":"<<strerror(errno)<<endl;
         break;
      }
   }
   //关闭不要的fd
  close(rfd);
  unlink(fifoname.c_str());//删除文件fifo


    return 0;
}

client.cc client

Since the pipeline file is created on the server side, there is no need to create a pipeline file on the client side

Just open the file directly, open the file in write mode


In order to avoid the presence of spaces in the entered words,
enter man fgetsthe command

Get a string from the specified stream and specify the size of the string


Since there are two executables, two terminals are required

When terminal 2 does not run the server, there is no pipeline file, but after terminal 1 runs the server, the pipeline file appears in terminal 1


When terminal 1 runs the client, enter the corresponding information, and it will be automatically displayed in terminal 2.
The client can send the information to the server.

full code

//客户端
#include<iostream>
using namespace std;
#include<sys/stat.h>
#include<sys/types.h>
#include<cerrno>
#include<cstring>
#include<fcntl.h>
#include<unistd.h>
#include<cstdio>
#include<cassert>
#include"comm.hpp"//公共路径
using namespace std;
int main()
{
    
    
   //不需要创建管道文件,打开文件即可
   int wfd=open(fifoname.c_str(),O_WRONLY);//以写的方式打开文件
   if(wfd<0)//说明打开失败
   {
    
    
     cout<<errno<<":"<<strerror(errno)<<endl;
     return 0;
   }

   //进行常规通信
   char buffer[NUM];
    while(true)
    {
    
    
        cout<<"请输入你的消息# ";
        char *msg=fgets(buffer,sizeof(buffer)-1,stdin);//将标准输入流的数据写入buffer中
        assert(msg);//检查是否为空
        (void)msg;//保证rlease模式发布依旧被使用
        //fgets会读取回车 即\n
        buffer[strlen(buffer)-1]=0;//12345\n  把\n位置覆盖为\0

        ssize_t n=write(wfd,buffer,strlen(buffer));
        assert(n>=0);
    }

   close(wfd);//关闭文件描述符
    return 0;
}

Guess you like

Origin blog.csdn.net/qq_62939852/article/details/130138087