Interprocess communication (anonymous pipeline) pipe function

1. Interprocess communication simply means that two or more processes can exchange data with each other

2. The general process is independent. The PCB(struct task_struct structure) of a process is also called the process control block, which only stores the information of the process. The physical memory mapped by the virtual address space through the page table is accessible only through the operating system. He can only access it. He doesn't know where the data is stored in the physical memory The operating system will only allow him to access his own space, and will not allow him to access the data saved by other processes

3. In order to communicate between processes, there is a pipeline. The following is an introduction to the pipeline

1. Pipe symbol |;

The essence of a pipeline is a buffer in the kernel, so why the kernel,

liru take an example: it's like a pupil changing seats. Originally, all seats are assigned by the teacher, but a wants to change to b's position, and b agrees, but the teacher is not allowed to change it without his permission, so they have to tell the teacher that a and b return the seats to the teacher, so a can get b's seat from the teacher In this case, the exchange of seats requires higher managers to allocate Therefore, the process needs to put the data to be exchanged in other processes before they can be used

For example, ps aux | grep command: ps aux puts the obtained data in the pipeline, and grep obtains the ps obtained data through the pipeline

2. Create the function of the pipe

int pipe(int pipefd[2]);

pipefd: the parameter is an array of two elements: pipe[0]: read end of pipe, pipe[1], write end of pipe The parameter type is an output type parameter, (the output type parameter only needs to be passed into the space, and the internal implementation parameter of the function has to be assigned)

Return value: 0 if the pipeline is created successfully and - 1 if it fails;

The read and write ends of the pipeline are essentially file descriptors. Only the read and write ends of the pipeline are owned To operate the contents of the pipeline This enables interprocess communication Because it is a file descriptor, it is stored in the PCB. So how can different processes have a pipeline to read and write? You can have it by copying a PCB Therefore, the basic interprocess communication is realized through the parent-child process

When the parent process creates a pipeline, it means that the parent process has a read-write end. After creating a child process, the child process also has a read-write end of the pipeline

Characteristics of pipes:

Half duplex: can only write in at the writing end and then slip out at the reading end

Anonymous pipes have no identifiers, so only related processes can communicate

The life cycle of the pipeline follows the whole process The parent-child process is two processes, so the pipeline will not be destroyed until the parent-child process exits

Pipe size: 64k

Reading the data in the pipeline is different from reading the data in the ordinary file. Reading the ordinary file is to take out a copy of the data in the file. Reading the pipeline is to take it out directly, and the read data will be lost in the pipeline

#include<stdio.h>

#if 0
int main()
{
    int fd[2];
    int pp=pipe(fd);
    char arr[]="s";
    int count=70000;
    while(count)
    {
        write(fd[1],arr,1);
        count--;
    }
    return 0;
}
#endif


int main()
{
    int fd[2];
    int pp=pipe(fd);
    char arr[1024];
    read(fd[0],arr,1);
    return 0;
}

They are writing all the time and reading all the time

The running results of both programs are:

Because there is no printing, there is no data, but neither program exits and there is no dead loop in the program So the reason for not quitting is in the write and read functions. When the pipeline is full, the write function will enter the blocking state. Blocking is waiting for a resource to continue running

Set to non blocking state:

int fcntl(int fd,int cmd,...)

fd: file descriptor to be operated

cmd: tell fcntl function what to do, including F_GETFL: get attribute information of file descriptor F_SETFL: set the attribute information of the file descriptor, and put the set new attribute information into the variable parameter list behind the function

Return value: pass in the macro to get the file descriptor. The return value is the attribute information of the file descriptor

Pass in the macro that sets the attribute information of the file descriptor. If the setting is successful, 0 is returned; Setting failed, return - 1;

When it is set to the non blocking state, if it is full or read, the function will return

Test non blocking status:

Case 1: the parent process only writes, writes a little, and the child process is read-only and always reads Therefore, turn off the reading of the parent process, turn off the writing of the child process, check the situation, and then turn off the writing of the parent process on this basis

 

Black path off: Code Verification

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<fcntl.h>
//Create a pipe and fork to create a child process. The parent process writes and the child process reads. The reading end of the child process is set to non blocking. The parent process turns off reading and the child process turns off writing (the parent process also turns off writing)
int main()
{
    int fd[2];
    int pd= pipe(fd);
    if(pd<0)
    {
        return 0;
    }
    int fk=fork();
    if(fk<0)
    {
        return 0;
    }
    else if(fk==0)
    {
        //child
        close(fd[1]);
        int A=fcntl(fd[0],F_GETFL);
        fcntl(fd[0],F_SETFL,A|O_NONBLOCK);
       // int a=5;
        char arr[1024];
             int b=0;
             b=read(fd[0],arr,3);
             printf("%s\n",arr);
             printf("%d\n",b);
       char arr1[1024];
       int c=read(fd[0],arr,3);
       printf("%d\n",c);
       printf("%s\n",arr1);
    }
    else
    {
        //parent
        //Close the reader
        close(fd[0]);
        //Close write end
       // close(fd[1]);
        wait(NULL);
    }
    return 0;
}

When it is set to non blocking: the difference from before is that after setting, if there is no content in the pipeline, the function will exit

What if the write side of the parent process is also closed? What will be written in before closing? What will happen to the subprocess

Release the last result of the above operation (FD [1])

Picture introduction

 

 

This will happen. Only the read end of the child process is left for the operation of the pipeline No more processes can be written in When the child process finishes reading, it will return 0;

 else
    {
        //parent
        //Close the reader
        close(fd[0]);
        char array[]="a";
        write(fd[1],array,1);
        
        //End write off
        close(fd[1]);
        wait(NULL);
    }

In the parent process of the above program, after writing content to the pipeline, close the write end and output

Note when the reading end of the pipeline is set to non blocking, the read function returns 0 if it is read out in the pipeline
 


In case 2, the parent process writes, and the write setting is non blocking. Turn off the reading of the parent process and the writing of the child process (on this basis, turn off the reading of the parent process)

code:

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
//Set the write side to non blocking
int main()
{
    int fd[2];
    int pp=pipe(fd);
    if(pp<0)
    {
        return 0;
    }
    int fk=fork();
    if(fk<0)
    {
        return 0;
    }
    else if(fk==0)
    {
        //close(fd[0]);
        close(fd[1]);
        //The reason for adding an endless loop here is that if the child process exits after closing the write side, there will be only one parent process left,
        //However, the parent process closes the only read end, so the effect is the same as that of the child process. The pipeline has no read end but only write end, so it crashes
        while(1)
        {

        }
    }
    else
    {
        sleep(3);
        close(fd[0]);
        int A=fcntl(fd[1],F_GETFL);
        fcntl(fd[1],F_SETFL,A|O_NONBLOCK);
        int count=70000;
        int wsize=0;
        while(count)
        {
            wsize=write(fd[1],"s",1);
            count--;
            if(wsize<0)
            {
                break;
            }
        }
        printf("%d\n",wsize);
        printf("i am ");
        //parent
    }
    return 0;
}

The running result is - 1 (the output of I am is useless). When the pipeline is full, it returns - 1

For example, if the read of the child process is also closed, only the write of the parent process can be operated on the pipeline. In this case, if it is full, 0 will be returned

Note: when setting fd[0] as non blocking, the parent process uses the wait function. When setting fd[1] as non blocking, only the write of the child process is closed, and a while loop is added The reason is that when running a process, another process cannot exit. If it exits, the process will lose its operation on the pipeline. There is only one process that can operate the pipeline. If it is not added, it is equivalent to directly closing the reading and writing of the process, and the running results of only closing one end cannot be tested

 

Keywords: Linux

Added by David Rech on Sat, 05 Mar 2022 05:29:45 +0200