Getting started with linux---named pipes

How to create a named pipe

You can use the mkfifo function to create a pipeline file in the program. The function is declared as follows: This function
Insert image description here
requires two parameters. The first parameter indicates the path under which the pipeline file is to be created and this path must contain the name of the pipeline file, because Each file has corresponding permissions, so the second parameter of the function represents the permissions of the pipeline file. If the pipeline file is successfully created, the function will return 0. If the creation fails, the function will directly return the corresponding error code: Then
Insert image description here
continue Next, let's create a pipeline file and take a look. The current path is as follows:
Insert image description here
Then we can create the pipeline file through the function mkfifo in the program:

#include<iostream>
#include<cerrno>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>    
using namespace std;
int main()
{
    
    
    umask(0);
    int n=mkfifo("/home/xbb/folder13/name_pipe",0600);
    if (n == 0)
    {
    
    
        cout<<"creat success"<<endl;
    }
    else
    {
    
    
        cout << "errno: " << errno << " err string: " << strerror(errno) << endl;
    }
    return 0;
}

Then the running result is as follows:
Insert image description here
You can see that after the program runs successfully, an additional file named name_pipe appears, and this file starts with p, then this p means pipe, which means that the current file is a pipe file, and use When you use the command ls -ilto view the inode of the file, you can also find that the file has an inode. This means that the pipeline file is an independent file, and the size of the current pipeline file is 0, which means that there is no data in the current file. Then using the following command we can Continuously output data to the screen: cnt=0; while :; do echo "hello world-> $cnt"; let cnt++; sleep 1;done:
Insert image description here
Then here we can use redirection to output the data originally output to the screen into the pipeline file:
Insert image description here
running the current command will continuously input data into the pipeline, but we then When you create a process that keeps checking the size of the pipe file, you will find that the size of the pipe file has not changed at all:
Insert image description here
However, although the size of the file has not changed at all, when we use the cat command to output the content of the file, it will You will find that the content we entered into the file is printed on the screen:
Insert image description here
then this is the characteristic of the pipe file, then let's take a look at the principle of named pipes.

The principle of named pipes

When we open a file in the operating system, the operating system will create a struct file object, which contains the buffer and file operation methods. Then there will be a pointer in the PCB in the process pointing to a file_struct. Structure, there is an array of pointers in the structure, and each element of the array points to the struct file object of a different file:
Insert image description here
But there is a problem here. If multiple processes open the same file, the operating system will create multiple struct file object? The answer is no. Even if multiple processes open the same file, the operating system will only create one struct file object, so this will cause multiple processes to share a struct file. If multiple processes share a struct file , then this is to allow multiple processes to see the same resource, so this strcut file is equivalent to a pipe, but the strcut file of the pipe file will not refresh the data in the internal buffer to the file on the disk. The data in the struct file object is written and read at the memory level. So how does the named pipe allow different processes to see the same resource? The answer is to let different processes open the same file with the specified name (path + file name). Path + file name = the only certain file. The reason why it is called a named pipe is because the pipe sees the same file through the file name. Resources, and anonymous pipes determine the uniqueness of parent-child processes through inheritance, not through file names, so they are called anonymous pipes.

Named pipe communication

The function mkfifo can create a named pipe in the program. Since it is created, there is a corresponding function to delete the pipe file in the same way. The unlink function can be used to delete the created pipe file. The declaration of the function is as follows: The parameters within the function are
Insert image description here
expressed The pipe file to be deleted will return 0 if the deletion is successful, and the corresponding error code will be returned if the deletion fails:
Insert image description here
Then here we can use these two functions to implement communication between processes. First create a file, which contains Two functions, one for creating pipeline files and one for deleting pipeline files:

#include<iostream>
#include<cerrno>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>    
using namespace std;
#define PIPE_PATH  "/home/xbb/folder13/name_pipe" 
bool createFifo(const string &path)
{
    
    

}

void removeFifo(string &path)
{
    
    
    
}

To eliminate the previous experience, we can easily implement the createFifo function. When the value returned by the mkfifo function is equal to 0, it returns true. If the return value of the function is non-0, it returns false:

bool createFifo(const string &path)
{
    
    
    umask(0);
    int n=mkfifo(path.c_str(),0600);
    if(n==0)
    {
    
    
        return true;
    }
    else
    {
    
    
        cout << "errno: " << errno << " err string: " << strerror(errno) << endl;
        return false;
    }
}

In the same way, the removeFifo function calls the unlink function to delete it. So here we use the assert function to determine whether the file is deleted successfully. The code here is as follows:

void removeFifo(const string &path)
{
    
    
    int n=unlink(path.c_str());	
    assert(n==0);
    (void)n;
}

So this is the content of the comm.hpp file. Next, we have to create the server.cc file and the client.cc file. The server.cc file is responsible for reading data from the pipe, and the client.cc file is responsible for writing data from the pipe. , because the server.cc file is the reading end, so we let it decide the creation and deletion of the pipeline. In this server.cc file, first use the creatFifo function in comm.hpp to create the pipeline, and then use assert to determine whether the creation is Success. After the creation is successful, use the open function to open the pipe file in reading mode, and then get the subscript of the reading end of this function.

#include"comm.hpp"
int main()
{
    
    
    int r = createFifo(PIPE_PATH);
    assert(r);
    (void)r;
    int rfd=open(PIPE_PATH, O_RDONLY);
    cout<<"开始读取"<<endl;
    if(rfd<0)
    {
    
    
        exit(1);
    }
    return 0;
}

At this point, our pipeline file has been successfully created and opened in the current process. Then we can create a while loop to continuously read information into the pipeline, because the read information needs to be stored in one place. , so before the loop we have to create an array to act as a buffer. Then in the loop, we can use the read function to read data from the file with the subscript rfd and put the read data into the buffer. Because there may be an end or error when reading data, a variable must be created here to record the return value of the read function. The code here is as follows:

#include"comm.hpp"
int main()
{
    
    
    int r = createFifo(PIPE_PATH);
    assert(r);
    (void)r;
    int rfd=open(PIPE_PATH, O_RDONLY);
    cout<<"开始读取"<<endl;
    if(rfd<0)
    {
    
    
        exit(1);
    }
    char buffer[1024]={
    
    0};
    while(true)
    {
    
    
        ssize_t s = read(rfd, buffer, sizeof(buffer)-1);
    }
    return 0;
}

Then use the if else statement to judge the value of size. If the value of size is greater than 0, it means that the current reading is correct. We directly output the value in the buffer. If the value of size is equal to 0, it means that the current reading is over. Directly use break to end the while loop. If the sizede value is less than 0, it means that there is an error in the current reading. Then we will print the error code to see where the problem is and use break to end the loop. After the loop ends, we will close the current open pipeline file, and use the removeFifo function to delete the pipeline file, then the code here is as follows:

#include"comm.hpp"
int main()
{
    
    
    int r = createFifo(PATH);
    assert(r);
    (void)r;
    int rfd=open(PATH, O_RDONLY);
    cout<<"开始读取"<<endl;
    if(rfd<0)
    {
    
    
        exif(1);
    }
    char buffer[1024]={
    
    0};
    while(true)
    {
    
    
        ssize_t s = read(rfd, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
    
    
            buffer[s] = 0;
            std::cout << "client->server# " << buffer << std::endl;
        }
        else if(s == 0)
        {
    
    
            std::cout << "client quit, me too!" << std::endl;
            break;
        }
        else
        {
    
    
            std::cout << "err string: " << strerror(errno) << std::endl;
            break;
        }
    }

    close(rfd);
    // sleep(10);
    removeFifo(NAMED_PIPE);
    return 0;
}

So this is the content of the server.cc file, and the same is true for the client.cc file. First open the pipe file in writing mode and then make a judgment, then create a buffer and write the information you want to write to the pipe first. In the buffer, use the write function to put the contents of the buffer into the writing pipe, and then judge whether the current writing is successful based on the return value of the write function. Because it needs to be written multiple times, a while loop must be added here. To write in a loop:

#include"comm.hpp"
int main()
{
    
    
    int Wfd=open(PIPE_PATH, O_WRONLY);
    if(Wfd < 0) exit(1);
    char buffer[1024];
    cout<<"client say"<<endl;
    while(true)
    {
    
    
        fgets(buffer, sizeof(buffer), stdin);
        if(strlen(buffer) > 0) 
        {
    
    
            //将末尾的/n去掉
            buffer[strlen(buffer)-1] = 0;
        }
        ssize_t n = write(Wfd, buffer, strlen(buffer));
        assert(n == strlen(buffer));
        (void)n;
    }
    close(Wfd);
    return 0;
}

After this file is written, we can complete the makefile. First, the operation of this function requires both files to generate executable programs, so here the instruction is compared to all. This instruction requires the client and server to generate executable programs:

.PHONY:
all: client server

However, there is no executable program under the current path, so we have to add the implementation methods and dependency files corresponding to the two executable programs:

.PHONY:
all: client server

client:client.cc
	g++ -o $@ $^ -std=c++11 -g
server:server.cc
	g++ -o $@ $^ -std=c++11 -g

Then there is the delete instruction, which just deletes the two generated executable programs. Then the entire content of the makefile is as follows:

.PHONY:
all: client server

client:client.cc
	g++ -o $@ $^ -std=c++11 -g
server:server.cc
	g++ -o $@ $^ -std=c++11 -g

.PHONY:
clean:
	rm -f client server

Then we can test it. First, use the make command to generate two executable programs:
Insert image description here
then open the server first and then the client program. Then you can see this phenomenon:
Insert image description here
because the write end is not open, the server has been blocked in open. In the function, when we run the client program, we can see the content printed by the server process:
Insert image description here
Insert image description here
Then we enter the content into the client process, and then we can see that the content in the input import client is output to the server:
Insert image description here
Insert image description here
then this is the naming Pipeline communication.

Supongo que te gusta

Origin blog.csdn.net/qq_68695298/article/details/132818520
Recomendado
Clasificación