Linux interprocess communication|pipe communication

Pipeline communication

1. Introduction to Pipelines

A pipe is a way of communication between processes in Linux, which connects the output of one program directly to the input of another program. There are two main types of pipes: unnamed pipes and named pipes

Unnamed pipe Famous pipe
A primitive method of pipe communication in Linux An improvement to the nameless pipe
Can only be used for communication between related processes Two unrelated processes can communicate with each other
Simplex communication mode with fixed reading and writing ends You can specify the read and write mode when opening the pipeline
You can use read()/write(), it does not belong to the file system and only exists in memory Through file IO operation, the content is stored in memory

Unnamed pipes and named pipes
Unnamed pipes and named pipes

2. Unnamed pipe

2.1 Unnamed pipe creation and closure

The communication method based on the file descriptor when the nameless pipe is used. When a pipe is created, it will create two file descriptors: fd[0] and fd[1], where fd[0] is fixed for the read pipe and fd[1] is fixed for the write pipe. This constitutes a one-way data channel.
Creating a pipe can be achieved by pipe()

/*****pipe()函数*****/
函数原型:int pipe(int fd[])
传 入 值:fd[]是包含两个元素的数组,存放管道对应的文件描述符
返 回 值:成功返回0;失败返回-1

When the pipe is closed, just use the close() function to close the two file descriptors.

2.2 Unnamed pipe read and write

Both ends of the pipe created with the pipe() function are in a process. Since pipes are mainly used for communication between different processes, usually a pipe is created first, and then the fork() function is called to create a child process, which will inherit the parent process’s pipe. At this time, the corresponding relationship between the file descriptors of the parent-child process pipeline is shown in the left figure below.
Due to the simplex working mode of unnamed pipes, the process can either only read the pipe or write to the pipe. Therefore, only one of them can be used (for example, it can be agreed that the parent process reads the pipe and the child process writes the pipe). In this way, the unused read or write file descriptors should be closed. As shown in the right figure below, the write end of the parent process and the read end of the child process are closed, and a pipeline of "the child process writes to the parent process to read" is established.
Insert picture description here
Pipeline read and write attention

  • It only makes sense to write data to the pipe when the read end of the pipe exists. Otherwise, the process that writes data to the pipeline will receive the SIGPIPE signal from the kernel
  • When writing data to the pipe, Linux will not guarantee the atomicity of the write. As long as the pipe buffer has space, the writing process will try to write data to the pipe. If the pipeline buffer is full, the write operation will always be blocked
  • When the parent and child processes are running, their order is not guaranteed. In order to ensure that the parent and child processes and the corresponding file descriptors are closed, the sleep() function can be called in the two processes

Example program

/*****pipe.c*****/
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_DATA_LEN 256

int main(){
    
    
	pid_t pid;
	int pipe_fd[2];
	char buf[MAX_DATA_LEN];
	const char data[] = "Pipe test program";
	int real_read,real_write;
	
	memset(buf,0,sizeof(buf));
	if(pipe(pipe_fd) < 0){
    
    		//创建管道
		perror("fail to pipe");
		exit(-1);
	}
	if((pid = fork()) == 0){
    
    	//创建子进程
		/*子进程关闭写描述符,并通过是子进程暂停1s等待父进程关闭相应的读描述符*/
		close(pipe_fd[1]);
		sleep(1);
		/*子进程读取管道内容*/
		if((real_read = read(pipe_fd[0],buf,MAX_DATA_LEN)) > 0)
			printf("%d bytes read from the pipe is '%s'\n",real_read,buf);
		close(pipe_fd[0]);	//关闭子进程读描述符
		exit(0);
	}else if(pid > 0){
    
    
		close(pipe_fd[0]);	//父进程关闭读描述符
		if((real_write = write(pipe_fd[1],data,strlen(data))) != -1)
			printf("parent wrote %d bytes:'%s'\n",real_write,data);
		close(pipe_fd[1]);	//父进程关闭写描述符
		waitpid(pid,NULL,0); //收集子进程退出信息
		exit(0);
	}
}

After compiling and executing, the result is as follows

linux@linux-virtual-machine:~/andy/proc$ ./pipe
parent wrote 17 bytes:'Pipe test program'
17 bytes read from the pipe is 'Pipe test program'

3. Famous pipes

3.1 Creation of a famous pipe

A well-known pipe (FIFO) can be created using the mkfifo() function, which is similar to the open() operation in a file, and can specify the path and access rights of the pipe. After the pipeline is successfully created, the functions open(), read() and write() can be used. As with ordinary files, O_RDONLY can be set in open() for pipes opened for reading, and O_WRONLY can be set in open() for pipes opened for writing

/*****mkfifo()函数*****/
函数原型:int mkfifo(const char *filename, mode_t mode)
传 入 值:filename 要创建的管道名
		 mode 管道的访问权限
返 回 值:成功返回0;失败返回-1
FIFO error message meaning
EACCESS The directory path specified by the parameter filename does not have executable permissions
EEXIST The file specified by the parameter filename already exists
ENAMETOOLONG The path name of the parameter filename is too long
ENOENT The file contained in the parameter filename does not exist
ENOSPC Insufficient free space in the file system
ENOTDIR The directory in the parameter filename path exists but is not a real directory
EROFS The file specified by the parameter filename exists in the read-only file system

Example program

/*****fifo_write.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#define MYFIFO	"/tmp/myfifo"	//有名管道文件名

int main(int argc, char *argv[]){
    
    	//参数为即将写入的字符串
	int fd;
	int nwrite;
	
	if(argc < 2){
    
    
		printf("Usage: ./fifo_write string\n");
		exit(-1);
	}

	if((fd = open(MYFIFO,O_WRONLY)) < 0){
    
    	//以只写方式打开FIFO管道
		perror("fail to open fifo");
		exit(-1);
	}

	if((nwrite = write(fd,argv[1],strlen(argv[1]+1))) >0)	//向管道写入字符串
		printf("Write '%s' to FIFO\n",argv[1]);
	close(fd);

	return 0;
}
/*****fifo_read.c*****/
/*头文件和宏定义同fifo_write.c*/
int main(){
    
    
	char buf[256];
	int fd, nread;
	/*判断有名管道是否已经存在,若为创建则以相应的权限创建*/
	if(access(MYFIFO,F_OK) == -1){
    
    	//管道文件不存在
		perror("fail to mkfifo");
		exit(-1);
	}

	if((fd = open(MYFIFO,O_RDONLY)) < 0){
    
    
		perror("fail to open fifo");
		exit(-1);
	}

	while(1){
    
    
		memset(buf,0,sizeof(buf));
		if((nread = read(fd,buf,256)) > 0)
			printf("Read '%s' from FIFO\n",buf);
	}
	close(fd);

	return 0
}

Run the above two programs in two terminals respectively. Start the read pipeline program first. After the pipeline is established, the reading process begins to read the content from the pipeline cyclically. If there is no data in the current pipeline, it is blocked until the write pipeline process writes data to the pipeline. After running the write pipeline program, the reading process can read the user's input from the pipeline. The program running results are as follows.
Terminal 1 is as follows

linux@linux-virtual-machine:~/andy/proc$ ./fifo_read
Read 'FIFO' from FIFO
Read 'Test' from FIFO

Terminal two is as follows

linux@linux-virtual-machine:~/andy/proc$ ./fifo_write FIFO
Write 'FIFO' to FIFO
linux@linux-virtual-machine:~/andy/proc$ ./fifo_write Test
Write 'Test' to FIFO

Guess you like

Origin blog.csdn.net/Chuangke_Andy/article/details/108305695