[LInux] Inter-process communication -- anonymous pipe

foreword

When we are learning process management and process replacement, we have emphasized it 进程的独立性. What is inter-process communication? This seems to be independent of the process, 相矛盾right?
So today, we will learn 进程间通信, and the first communication method –管道

insert image description here

1. Inter-process communication

Inter-process communication does not destroy the independence of the process, which we are 管道explaining .
The purpose of process communication is as follows:

  1. 数据传输: A process needs to convert its数据发送给另一个进程
  2. 资源共享: between multiple processes共享同样的资源
  3. 通知事件: A process needs to send a message to another process or a group of processes, 通知it (they) happened 某种事件(such as to notify the parent process when the process terminates)
  4. 进程控制: Some processes want to complete 控制另一个进程的执行(such as Debug process), at this time the control process wants to be able to 拦截另一个进程的所有陷入和异常find its state changes in time.

interprocess communication三种常用方法

  1. 管道

anonymous pipe
named pipe

  1. System V进程间通信

System V message queues
System V shared memory
System V semaphores

  1. POSIX进程间通信

message queue
shared memory
information number
mutex
condition variable
read-write lock

进程依旧具有独立性, It is impossible for a process to directly obtain data from the heap and stack area of ​​another process, so how is inter-process communication realized?
To allow two different processes to communicate, the prerequisite is: first let the different processes,看到同一份“资源”

The pipeline is one of the ways to see the same "resource"
insert image description here

2. Pipeline

Pipeline is the oldest form of inter-process communication in Unix.
We call a data stream connecting one process to another process a "pipeline". What
we use in Linux commands |is a pipeline
who | wc -l. You can whocheck how many users are logged in to the server. wcThere are several lines of text for statistics.
Here is the who creation process. It first displays how many users there are, and then transmits the data to wc. After wc finishes processing, it outputs the result
insert image description here

Let's understand the pipeline more concretely

When we create a process, OS will create task_structmaintenance, management process. And there is one in the structure strust file_struct*, which points to a structure, which manages files and stores open files in it 文件描述符, 默认0,1,2分别是标准输出,标准输入,标准错误
and everything under Linux is a file, 管道也是文件but it is for the OS to realize inter-process communication 临时创建的一个内存文件. The default is 空闲文件描述符的后两个. As shown below

insert image description here

To create a child process, you need to recreate task_struct, but the content of struct files_struct is copied from the parent process, but it is only a copy.拷贝一份文件描述符和文件的映射关系,不会重新创建新文件
fork创建子进程后,只会赋值进程相关的数据结构对象,不会复制父进程曾经打开的文件对象!就像浅拷贝一样

insert image description here

This pipeline, 只支持单向通信, is called 匿名管道. Because of the file 只有一个缓冲区, only one item can be read and written at the same time,
so we need to manually determine the data flow direction and close the unnecessary file descriptor fd

3. Use of anonymous channels

Next, let's briefly simulate inter-process communication.

To achieve process communication with anonymous pipes, 父进程创建匿名管道the method of creation is to usepipe函数
insert image description here

The parameters of this function are special: Yes 输出型参数. Similar to the status of waitpid.
We pass an integer array of size 2,pipe函数内部会将创建的管道的读和写两个文件描述符写入这个数组,返回给我们

Return value: successful call, return 0; error, return -1, and set 错误码.

1. The use of pipe

Let's first use the pipe function to see its effect

#include<iostream>
#include<cerrno>
#include<unistd.h>
#include<string.h>

using std::cout;
using std::endl;

int main()
{
    
    
    //创建管道所需传参的数组
    int pipefd[2]={
    
    0};
    
    //1.创建管道
    int n=pipe(pipefd);
    if(n<0)
    {
    
    
        //如果返回值小于0,即-1,还会设置错误码
        //我们再把错误码对应的错误信息,打印一下
        cout<<"pipe error,"<<errno<<":"<<strerror(errno)<<endl;
        return 1;
    }
    cout<<"pipefd[0]:"<<pipefd[0]<<endl;
    cout<<"pipefd[1]:"<<pipefd[1]<<endl;

    return 0;
}

insert image description here
As mentioned earlier, the pipe's two file descriptors, 默认使用当前空闲的前两个文件描述符.
Two file descriptors created by the pipeline, default第一个是读,第二个是写

mnemonic
pipe[0]的是读端,0 -> 嘴巴 -> 读
pipe[1]的是写端,1 -> 笔 -> 写

2. Prepare for correspondence

We know how to create a pipeline, and then we can prepare to implement communication between parent and child processes.
As we mentioned above, the prerequisite for inter-process communication is to allow different processes to see the same resource.
The pipeline can be this resource. We simulate the communication between parent and child processes, let the child process write data into the pipeline, and then the parent process receives the data

code show as below:

#include<iostream>
#include<cerrno>
#include<cassert>
#include<unistd.h>
#include<string.h>
#include<string>
#include<sys/types.h>

using std::cout;
using std::endl;

int main()
{
    
    
    //创建管道所需传参的数组
    int pipefd[2]={
    
    0};
    
    //1.创建管道
    int n=pipe(pipefd);
    if(n<0)
    {
    
    
        //如果返回值小于0,即-1,还会设置错误码
        //我们再把错误码对应的错误信息,打印一下
        cout<<"pipe error,"<<errno<<":"<<strerror(errno)<<endl;
        return 1;
    }
    cout<<"pipefd[0]:"<<pipefd[0]<<endl;//读端
    cout<<"pipefd[1]:"<<pipefd[1]<<endl;//写端

    //2.创建子进程
    pid_t id = fork();
    //获取错误。意料之外,使用if;意料之中,用assert
    //此处应该使用if,但为了简单一些,使用assert
    assert(id!=-1);

    if(id==0)
    {
    
    
        //子进程
        
        //3.关闭不需要的fd
        close(pipefd[0]);//关闭子进程的读端

        //4.开始通信
        const std::string namestr="hello ,我是子进程";
        int cnt=1;//计数器

        char buffer[1024];//write的字符数组

        while(true)
        {
    
    
            //将内容写入buffer字符串
            snprintf(buffer,sizeof(buffer)-1,"%s,计数器:%d,我的PID:%d\n",namestr.c_str(),cnt++,getpid());
            //将内容写入管道
            write(pipefd[1],buffer,strlen(buffer));
            sleep(1);
        }

        //关闭子进程的写端,再exit退出
        close(pipefd[1]);
        exit(0);
    }

    //父进程

    //3.关闭不需要的fd
    //让父进程进行读取
    close(pipefd[1]);//关闭父进程的写端

    //4.开始通信
    char buffer[1024];
    while(true)
    {
    
    
        //读取的大小,至少要留一个位置写入\0
        int n = read(pipefd[0],buffer,sizeof(buffer)-1);
        if(n>0)
        {
    
    
            buffer[n]='\0';
            cout<<"我是父进程, child give me a message: "<<buffer<<endl;
        }
    }

    
    //关闭父进程的读端,结束进程
    close(pipefd[0]);
    return 0;
}

insert image description here
In this way, we have realized the communication between the parent and child processes.

3. Features and scenarios of anonymous pipes

  1. When we comment out the sleep(1) that the child process writes data to. The result of the program running becomes different.
    insert image description here
    We see that the child process writes many times, and the parent process only reads once.
    Let's change it again. After the child process writes, the sleep is changed to sleep(5)
    . When the parent process reads data, we read 10 bytes and 10 bytes.
    insert image description here
    The program running result changes again
    insert image description here

These two experiments have verified such a conclusion:
In the communication of anonymous pipelines, 写入的次数,和读取的次数,不是严格匹配的there is no strong correlation between the number of reads and writes—because the buffer reads and writes are 字节in units—字节流

  1. Next, let's do another experiment:
    we let the parent process read data normally, but the child process writes every time at an interval of 10 seconds.
    insert image description here
    Observing the operation, we found that we made the writing of the child process slower, but the reading of the parent process also slowed down. What's going on here?

First of all, the data of the pipeline file is similar to a queue. Writing once is entering the queue, and reading is exiting the queue. 只要读取,数据就没了。
Therefore,子进程写入变慢,父进程没有东西可读,就进入了阻塞状态。

  1. Let's make the reading of the parent process slower, and the child process writes normally
    insert image description here
    insert image description here
    insert image description here

We found that 65536(从0开始)次after the pipeline was written, it was not written. When the parent process time is up, we read a lot of X
and every time we write, we only write 1 byte. So the pipe can write at most 65536字节, that is 2的16次方, 64kb,16个数据块。

  1. What happens if we close the write end of the child process and continue the read end of the parent process?
    We let the child process write data once and then close the write terminal. The parent process still reads data all the time, but it needs to make an extra judgment on the return value of read. The running
    insert image description here
    results are as follows
    insert image description here

When we 关闭子进程的写端,父进程再读取就会读到文件尾,就会返回0, the parent process terminates.

  1. What happens if we close the read end of the parent process?

Straight to the conclusion:
when a pipeline has only a writing end and no reading end, it means that no matter how it is written, no one will get 没有意义it 操作系统不会维护无意义的,低效率的,或者浪费资源的事情. The OS will kill this process that has been writing!通过13号信号 SIGPIPE,杀死进程

Next, we summarize the characteristics and scenarios of anonymous pipes

特点

  1. 单向通信, 半双工in one case, only one of the two parties can write at the same time.
    Because of the anonymous pipe 只有一个缓冲区, only one party can read and write at the same time.
    全双工, both parties can simultaneously write
  2. The essence of anonymous pipes is that 文件, because fd的生命周期随进程, so管道的生命周期也是随进程的。
  3. Anonymous pipe communication is usually used for communication 具有“血缘关系”的进程之间的进程通信, because anonymous pipes are 内存级文件, so only the created child process can obtain the anonymous pipe created by the parent process, so it is often used 父子间通信 -- pipe 打开匿名管道。
    4. In the communication of anonymous pipes, 写入的次数,和读取的次数,不是严格匹配的the number of reads and writes is not strongly related — Because the buffer reads and writes 字节in units —字节流
  4. Have a certain 协同能力, so that read and write can communicate according to certain steps—自带同步机制

场景

  1. If we read, the reading end has finished reading all the pipeline data, if the other party does not send it, the reading end will堵塞
  2. If we write, write to the end, 管道写满了then 暂时we can’t continue to write. If necessary 读端读取数据, the read data is removed from the pipeline, and we can continue to write
  3. If we 关闭了写端, 读取完毕管道数据, read again, it will be read 文件尾, and read will return 0.
  4. The write end keeps writing, 读端关闭and the operating system will send 13号 SIGPIPE信号the process to kill the write end.

When the amount of data written in a single time is not greater than PIPE_BUF, LInux will guarantee that the amount of data written 原子性
is greater than PIPE_BUF, and Linux will no longer guarantee the atomicity of writing.
目前的理解是,保证写入时不会被读取

conclusion

This concludes the content of this blog.

If you think this article is helpful to you, you might as well like it to support the blogger, please, this is really important to me.
insert image description here

Guess you like

Origin blog.csdn.net/m0_72563041/article/details/130094402