Linux - interprocess communication (pipeline)

Posted by generic88 on Fri, 04 Mar 2022 14:34:27 +0100

premise

Process multiple business modules at the same time. Multiple business modules may need to transfer data to each other or need synchronous control.
Two entities need to communicate with each other and must have resources that can be shared.
Files on disk: -- > IO processing is slow and out of sync.

Application scenario of process communication

  • Data transmission: a process needs to send its data to another process, and the amount of data sent is between one byte and several megabytes.
  • Shared data: multiple processes want to operate the shared data. If one process modifies the shared data, other processes should see it immediately.
  • Notification event: a process needs to send a message to another process or group of processes to notify it (them) of an event (such as notifying the parent process when the process terminates).
  • Resource sharing: multiple processes share the same resources. To do this, the kernel needs to provide locking and synchronization mechanisms.
  • Process control: some processes want to fully control the execution of another process (such as Debug process). At this time, the control process wants to be able to intercept all traps and exceptions of another process and know its state changes in time.

Interprocess communication mode

  1. The Conduit
    Named pipeline - > communication between any two processes
    Anonymous pipeline - > key communication between parent and child processes
  2. Semaphore
  3. Shared memory
  4. Message queue
    ——————————2, 3 and 4 all use the kernel space of the system to create kernel objects
  5. socket network programming

1. Named pipe

There is an identification of pipeline file on the disk. The transfer data is stored in memory and will not be stored in disk space.
To create a pipe file:
① Command:

	mkfifo name

② System call:

int mkfifo(const char *filename,int mode);

Operation of pipeline file:

open read write close

The first three will block operation:
open will block and must appear in pairs;
Read will block and can only be read after being written in;
write will also block, that is, when the memory space is insufficient, the memory space of the pipeline will be exhausted;

Open pipeline file: due to the half duplex characteristic of pipeline, the flag can only be O_RDONLY and O_WRONLY

int open(cosnt char *path,int flag)

Principle:
A pipe is a buffer managed by the kernel, which is equivalent to a piece of paper we put into memory. One end of the pipe is connected to the output of a process. This process puts information into the pipeline. The other end of the pipeline is connected to the input of a process, which takes out the information put into the pipeline. A buffer does not need to be large. It is designed as a ring data structure so that the pipeline can be recycled. When there is no information in the pipeline, the process reading from the pipeline will wait until the process at the other end puts the information. When the pipeline is full of information, the process trying to put the information will wait until the process at the other end takes out the information. When both processes end, the pipeline disappears automatically.



As can be seen from the above figure, there is only one buffer, and the pipeline is a half duplex communication mode
Exercise: process A accepts the data input by the user, and process B converts all the strings input by the user into uppercase and outputs them. The loop is executed, and the end input by the user ends.
A.c

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<fcntl.h>
#include<unistd.h>

int main()
{
	int fd = open ("./FIFO",O_WRDONLY);
	assert(fd != -1);
	
	while(1)
	{
		printf("Please Input:");
		char buff[128] = {0};
		fgets(buff,127,stdin);
		
		if(strncmp(buff,"end",3) == 0)
		{
			break;
		}
		write(fd,buff,strlen(buff) - 1);
	}
	close(fd);
	exit(0);
}

B.c

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<fcntl.h>

int main()
{
	int fd = open ("./FIFO",O_RRDONLY);
	assert(fd != -1);
	
	while(1)
	{
		char buff[128] = {0};
		int n = read(fd,buff,127);//The write end hangs up (closes the pipeline file), and the data read at this time is 0
		//When the other party does not hang up and just does not write data, it will only block and will not return 0
		if(n == 0)
		{
			break;
		}
		printf("buff::%s\n",buff);
	}
	close(fd);
	exit(0);
}

Running A program alone will be blocked because the pipeline files must be opened at the same time to complete communication.
The reading operation can be completed only when the two terminals open A and B respectively.

To sum up, the two blocking modes of famous pipelines are:

  • When only one end is opened, it will block
  • When there is no data in the pipeline, the read operation will be blocked at this time

2. Unknown pipeline

It can only be used between related processes. It is also a half duplex communication mode. In fact, famous pipes can be used for communication between parent and child processes, but it is not convenient. However, it will be more convenient to use nameless pipes for communication between parent and child processes.
Creation and opening of unknown pipe:

int pipe(int fds[2])->Formal parameters are two integer arrays. 2 just gives the user a hint: only two integer values (two file descriptors) are required

0 is returned for success and - 1 is returned for failure

  • fds[0]: file descriptor, pointing to the reading end of the pipeline
  • fds[1]: file descriptor, pointing to the write end of the pipeline

Therefore, the pipe will not be blocked, and the read and write ends will appear at the same time.
The general framework is as follows:

int fds[2];
pipe(fds);		
->Without blocking, you can directly create and open the nameless pipeline: there are both read and write ends;
This sentence must be written in fork before,fork The child process can inherit the file descriptor opened by the parent process

pid = fork();	
->Four file descriptors are generated, so close First turn off a pair of read and write, and then read,write

if(pid == 0)	
{
	close();	
	afferent fds[1]When, the parent process writes, the child process reads and passes in fds[1]Time is on the contrary
}
else
{
	close();
}

Some differences from famous pipelines:
Named pipeline: it can be applied between any two processes with permission to operate pipeline files.
Anonymous pipeline: it can only be applied between two related processes.
Unknown pipeline implementation:
Exercise: process A accepts the data input by the user, and process B converts all the strings input by the user into uppercase and outputs them. The loop is executed, and the end input by the user ends.

#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
	int fds[2];
	assert(pipe(fds) != -1);

	pid_t pid = fork();
	assert(pid != -1);

	if(pid == 0)//Subprocess reading data
	{
		close(fds[1]);

		while(1)
		{
			char buff[128] = {0};

			int n = read(fds[0],buff,127);//Read data from the reading end fds[0] of the unknown pipe
			if(n <= 0)
			{
				break;
			}
			printf("child::%s\n",buff);
		}

		close(fds[0]);
	}
	else
	{
		close(fds[0]);//The parent process writes data and closes the reader

		while(1)
		{
			printf("Please Input:");
			char buff[128] = {0};
			fgets(buff,127,stdin);

			if(strncmp(buff,"end",3) == 0)
			{
				break;
			}
			write(fds[1],buff,strlen(buff) - 1);//Write the data in the buff to the unknown pipe through fds[1]
		}
		close(fds[1]);
	}
	exit(0);
}

The problem with this code is that the child process does not read data, but the parent process continues to execute, resulting in a problem in the terminal display. Asynchronous IO is needed to solve this problem.

Topics: Linux Database MySQL SQL