12.1 Linux_进程间通信_管道

概述

什么是无名管道:

无名管道就是在内核中开辟了一块内存,进程1和进程2都可以访问这一块空间,从而实现通信。

当无名管道被创建时,父进程fd0指向管道的读端,fd1指向管道的写端。fork创建子进程后,子进程也有一对fd0指向管道的读端,fd1指向管道的写端。具体结构如下:

之后可以使一个进程关闭读端,另一个进程关闭写端。关闭后的具体结构如下:

无名管道(pipe)的特点:

  • 只能用作父子进程、兄弟进程之间的通信。
  • 单工通信,具有固定的读端和写端。即:只能一段读一段写,如果想读写都可以,则需要两条管道。
  • 创建无名管道时会返回2个文件描述符,分别用于读写管道

有名管道(fifo)的特点:

  • 解决了无名管道只能用作父子进程、兄弟进程之间的通信的问题
  • 有名管道通过路径名操作(管道文件p),在文件系统中可见,但内容存放在内存中。
  • 遵循先进先出规则。
  • 不支持lseek操作光标位置。
  • 单工通信。

无名管道

1、创建

cpp 复制代码
int pipe(int pipefd[2]);

返回值:成功返回0,失败返回EOF

pipefd:返回的文件描述符,pipefd0用于读,pipefd1用于写

注意:对于一个进程,只能使用一个文件描述符。即:该进程要么读管道,要么写管道,不能同时读写。

2、读写

读写管道就是使用文件I/O,像普通文件一样进行读写。

读管道特性:

  • 管道中有数据,read返回实际读取到的字节数
  • 管道中无数据,且写端全部关闭,read返回0
  • 管道中无数据,但写端没有关闭,read阻塞等待

写管道特性:

  • 写管道时,读端全部关闭,进程异常终止,信号为SIGPIPE(管道破裂)。
  • 写管道时,如果管道已满,write阻塞等待。管道大小为64K
  • 写管道时,如果管道未满,write返回实际写入的字节数

实验代码

无名管道实验代码如下:

cpp 复制代码
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(){
	int pfd[2];
	pid_t pid;
	char buf[100] = {0};
	//创建无名管道
	if(pipe(pfd)<0){
		perror("pipe");
		return -1;
	}
	pid = fork();
	if(pid<0){
		perror("fork");
		return -1;
	}else if(pid == 0){
		while(1){
			write(pfd[1],"pipe test",strlen("pipe test"));//写管道
			close(pfd[0]);//关闭读
            sleep(1);
		}
	}else{
		while(1){
			read(pfd[0],buf,sizeof(buf));//读管道
            close(pfd[1]);//关闭写
			printf("read:%s\n",buf);
		}
	}
	return 0;
}

有名管道

1、创建

cpp 复制代码
int mkfifo(const char *pathname, mode_t mode);

返回值:成功返回0,失败返回EOF

pathname:文件路径,不要新建在共享目录下。

mode:文件权限,实际权限 = mode & (~umask)

2、打开

打开管道文件就是使用使用文件I/O的open函数。

注意:open的flag参数只能传入O_RDONLY或O_WRONLY,因为管道只能一端读或一端写。

注意:当open的flag没有加上O_NONBLOCK时,open进入阻塞状态,直到有另一个进程以对应的只读或只写打开。即:进程A只读打开时阻塞,直到进程B只写打开;或者进程A只写打开时阻塞,直到进程B只读打开。

3、读写

读写管道就是使用文件I/O,像普通文件一样进行读写。

写入数据完整性问题:

在多进程写入管道时,当写入数据的大小 <= PIPE_BUF(4K)时,系统会自动保证数据的完整性,即要么全部写入,要么一个不写。当写入数据 > 4K时,不能自动保证数据完整性。

实验代码

writefifo.c代码如下:

cpp 复制代码
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#define FIFO_PATH "./fifo"
int main(){
	
	int fd;
	//创建有名管道
	if(mkfifo(FIFO_PATH,0777)<0){
		perror("mkfifo");
	}
	//打开管道文件
	if((fd = open(FIFO_PATH,O_WRONLY)) < 0){//以只读方式打开
		perror("open");
	}
	//写管道
	while(1){
		write(fd,"write fifo",strlen("write fifo"));
		printf("has write\n");
		sleep(1);
	}
	//关闭文件
	close(fd);

	return 0;
}

readfifo.c代码如下:

cpp 复制代码
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#define FIFO_PATH "./fifo"
int main(){
	
	int fd;
	char buf[100];
	//打开管道文件
	if((fd = open(FIFO_PATH,O_RDONLY)) < 0){//以只写方式打开
		perror("open");
	}
	//读管道
	while(1){
        //有数据才操作,防止写端关闭read不进行阻塞导致刷屏
		if(read(fd,buf,sizeof(buf)-1)>0){
			printf("read:%s\n",buf);
			memset(buf,0,sizeof(buf));
		}
	}
	//关闭文件
	close(fd);

	return 0;
}

代码运行结果如下:

相关推荐
Elastic 中国社区官方博客4 分钟前
用两行 JSON 替换你的 ILM 策略:数据流生命周期新增冻结层支持
大数据·运维·elasticsearch·搜索引擎·架构·全文检索
小趴菜一只12 分钟前
linux权限维持
linux·运维·服务器
小张同学a.14 分钟前
ELK企业级日志分析平台3——ES数据备份 & 集群监控 & ELFK+Kafka 架构部署
linux·运维·elk·elasticsearch·架构·kafka·filebeat
JenKinJia19 分钟前
Pycharm中增加远程服务器解释器无法连接的问题
服务器·pycharm·github
神龙斗士24022 分钟前
Socket编程:客户端与服务器通信全解析(网络编程)
运维·服务器·网络·socket
小则又沐风a24 分钟前
负载均衡式在线OJ---------第二幕
linux·c++·后端
中才实用32 分钟前
Jenkins自动化部署
运维·自动化·jenkins
snow@li33 分钟前
Kibana:功能全景梳理/Elastic Stack可视化管理平台
运维
AC赳赳老秦37 分钟前
官方技术文档聚合实践:用 OpenClaw 批量抓取开源项目文档,构建离线可检索技术知识库
java·运维·服务器·python·信息可视化·deepseek·openclaw
fengkai454542 分钟前
Linux服务--5.Nginx 服务
linux·运维·服务器