Linux进程间通讯|管道通讯

管道通讯

1. 管道简介

管道是Linux中进程间通讯的一种方式,它把一个程序的输出直接连接到另一个程序的输入。管道主要包括两种:无名管道和有名管道

无名管道 有名管道
Linux中管道通讯的一种原始方法 对无名管道的一种改进
只能用于具有亲缘关系的进程之间通讯 可以使互不相关的两个进程实现彼此通讯
单工的通讯模式,有固定的读端和写端 打开管道时可指定读写方式
可使用read()/write(),不属于文件系统只存在于内存中 通过文件IO操作,内容存放在内存中

无名管道和有名管道
无名管道和有名管道

2. 无名管道

2.1 无名管道创建与关闭

无名管道时基于文件描述符的通讯方式。当一个管道建立时,它会创建两个文件描述符: fd[0] 和 fd[1],其中fd[0]固定用于读管道,fd[1]固定用于写管道。从而构成了一个单向的数据通道。
创建管道可以通过 pipe()来实现

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

管道关闭时只需用 close() 函数将两个文件描述符关闭即可

2.2 无名管道读写

用pipe()函数创建的管道两端处于一个进程中。由于管道主要是用于不同进程间的通讯,通常先是创建一个管道,再调用 fork() 函数创建一个子进程,该子进程会继承父进程的管道。这时父子进程管道的文件描述符对应关系如下左图所示。
由于无名管道时单工的工作模式,即进程要么只能读管道,要么只能写管道。因此只能使用其中一个(例如可约定父进程读管道,子进程写管道)。这样就应该把不使用的读端或写端的文件描述符关闭。如下右图所示,父进程的写端和子进程的读端关闭,建立了一条“子进程写入父进程读取”的管道。
在这里插入图片描述
管道读写注意点

  • 只有在管道的读端存在时,向管道写入数据才有意义。否则,向管道写入数据的进程将收到内核传来的SIGPIPE信号
  • 向管道写入数据时,Linux将不保证写入的原子性,管道缓冲只要有空间,写进程就会试图向管道写入数据。如果管道缓冲区已满,那么写操作将会一直阻塞
  • 父子进程在运行时,它们的先后次序并不能保证。为确保父子进程以及关闭了相应的文件描述符,可在两个进程中调用sleep()函数

实例程序

/*****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);
	}
}

编译并执行后,结果如下

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. 有名管道

3.1 有名管道的创建

有名管道(FIFO)可以使用 mkfifo() 函数来创建,该函数类似于文件中的 open() 操作,可以指定管道的路径和访问权限。管道创建成功后,就可以使用 open()、read() 和 write() 这些函数了。与普通文件一样,对于为读而打开的管道可以在 open() 中设置 O_RDONLY,对于为写而打开的管道可在 open() 中设置 O_WRONLY

/*****mkfifo()函数*****/
函数原型:int mkfifo(const char *filename, mode_t mode)
传 入 值:filename 要创建的管道名
		 mode 管道的访问权限
返 回 值:成功返回0;失败返回-1
FIFO出错信息 含义
EACCESS 参数filename所指定的目录路径无可执行的权限
EEXIST 参数filename所指定的文件已存在
ENAMETOOLONG 参数filename的路径名称太长
ENOENT 参数filename包含的文件不存在
ENOSPC 文件系统的剩余空间不足
ENOTDIR 参数filename路径中的目录存在但却非真正的目录
EROFS 参数filename指定的文件存在于只读文件系统内

实例程序

/*****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
}

把以上两个程序分别在两个终端运行。先启动读管道程序,读进程在建立管道后就开始循环地从管道里读出内容。如果当前管道中没有数据,则一直阻塞到写管道进程向管道写入数据。在运行了写管道程序后,读进程能够从管道读出用户的输入内容,程序运行结果如下
终端一如下

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

终端二如下

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

猜你喜欢

转载自blog.csdn.net/Chuangke_Andy/article/details/108305695