Linux应用层开发 --- 管道

管道:属于 Linux 古老的IPC 进程间通信方式,本质是内核开辟的一块缓冲区,充当进程之间数据传输的通道,分为无名管道(pipe)、有名管道(FIFO)两种。

一、无名管道

无名管道: 只能用在具有亲缘关系的进程间通信(父子进程)

1. 本质

管道本质是内核中的一段缓冲区,默认64K字节,遵循队列存储方式(FIFO,先进先出)

2. 特点

(1)写阻塞:当管道的读写端都存在时,管道写满数据时,发生写阻塞

(2)读阻塞:当管道的读写端都存在时,管道为空时,读管道发生读阻塞

(3)读返回0:当管道没有写端,只有读端时,管道中有数据则读出数据,无数据时,不再读阻塞,读管道直接返回0字节

(4)管道破裂:当管道没有读端,向管道中写数据,发生管道破裂异常

3. 操作流程

(1)创建并打开管道:pipe();

#include <unistd.h>

int pipe(int pipefd2);

功能:创建并打开一个无名管道

参数:

pipefd:管道读端和写端的文件描述符

pipefd0:读 pipefd1:写

返回值:

成功:0

失败:-1

(2)读管道:read();

(3)写管道:write();

(4)关闭管道:close();

示例:

复制代码
int main(void)
{
	int pipefd[2];
	int ret = pipe(pipefd);
	if(-1 == ret)
	{
		perror("pipe error");
		return -1;
	}
	pid_t pid = fork();
	if(pid > 0)
	{
		close(pipefd[0]);//关闭读端
		write(pipefd[1], "hello son", 9);
		close(pipefd[1]);
	}
	if(0 == pid)
	{
		close(pipefd[1]);//关闭写端
		char buff[1024] = {0};
		read(pipefd[0], buff, sizeof(buff));
		printf("buff = %s\n", buff);
		close(pipefd[0]);
	}
	else
	{
		perror("fork error");
	}
	return 0;
}

二、有名管道

有名管道: 可以用在同一主机,任意进程间通信。

1. 阻塞特性

有名管道默认阻塞打开

  • 以只读 (O_RDONLY)打开管道:程序会阻塞等待,直到另一个进程以只写 (O_WRONLY)打开该管道。
  • 以只写 (O_WRONLY)打开管道:程序会阻塞等待,直到另一个进程以只读 (O_RDONLY)打开该管道。

通俗讲:读端等写端、写端等读端,两边都到位之后,通信才开始。

2. 读写行为规则

(1)写管道 :管道缓冲区满的时候,write()会阻塞,直到读端取走一部分数据。

(2)读管道

管道内有数据:read立刻读到数据返回。

管道为空:默认阻塞,等待写端写入数据。

(3)断开情况

读端全部关闭,写端继续写数据 → 写进程收到 SIGPIPE 管道破裂信号,进程异常终止

写端全部关闭,读端去读管道 → read 返回 0,代表读到文件末尾

3. 缓冲区大小

有名管道数据存放在内核缓冲区,磁盘上的 fifo 文件只作为一个入口标识,不会存储传输的数据。 默认缓冲区大小:4096字节,数据读完,缓冲区的数据就消失。

4. 半双工特性

有名管道属于半双工通信,同一时刻只能单向传递数据。

如果需要双向收发,必须创建两条独立的有名管道,一条负责 A→B,另一条 B→A。

5. 生命周期

  • mkfifo 创建出来的管道文件永久保存在磁盘;
  • 进程退出不会自动删除管道文件;
  • 用完后需要手动删除:remove("myfifo") 或者终端命令 rm myfifo

6. 操作流程

(1)创建有名管道文件:mkfifo();

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

权限赋0664就可以

返回值:

成功:0

失败:-1

(2)打开管道文件:open();

(3)写管道文件:write();

(4)读管道文件:read();

(5)关闭管道文件:close();

补充:remove();删除一个文件或目录

练习1:一个进程从终端读数据、另一个进程打印该数据

复制代码
###read.c
int main(void)
{
    int ret = mkfifo("fifo",0664);
    if(-1 == ret && errno != EEXIST)
    {
        perror("mkfifo error");
        return -1;
    }

    int fd = open("fifo",O_RDONLY);
    if(-1 == fd)
    {
        perror("open fifo error");
        return -1;
    }
    char buff[1024] = {0};
    while(1)
    {
        memset(buff,0,sizeof(buff));
        size_t sizeread(fd,buff,sizeof(buff));
        if(size <= 0)//=0 close(write)  <0 error
        {
            break;
        }
        printf("buff = %s\n",buff);
    }
    close(fd);
    remove("fifo");

    return 0;
}

###write.c
int main(void)
{
	int ret = mkfifo("fifo", 0664);
	if (-1 == ret && errno != EEXIST)
	{
		perror("mkfifo error");
		return -1;
	}

	int fd = open("fifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("open fifo error");
		return -1;
	}
    char buff[1024] = {0};
    while(1)
    {
        fgets(buff, sizeof(buff), stdin);
        if(0==strcmp(buff,".quit\n"))
        {
            break;
        }
	    write(fd,buff,strlen(buff));
    }
	close(fd);

	//remove("fifo");

	return 0;
}

练习2:将一个.png图片通过管道拷贝

复制代码
#####write.c

int main(int argc, const char *argv[])
{
	if (argc < 2)
	{
		printf("Usage : ./a.out <filename>\n");
		return -1;
	}

	mkfifo("myfifo", 0664);
	
	int fdfifo = open("myfifo", O_WRONLY);
	if (fdfifo < 0)
	{
		perror("open fifo error");
		return -1;
	}

	int fd = open(argv[1], O_RDONLY);
	if (fd < 0)
	{
		perror("open error");
		return -1;
	}

	char buff[1024] = {0};
	ssize_t ret = 0;
	while ((ret = read(fd, buff, sizeof(buff))) > 0)
	{
		write(fdfifo, buff, ret);
	}

	close(fd);
	close(fdfifo);

	return 0;
}




#####read.c

int main(int argc, const char *argv[])
{
	if (argc < 2)
	{
		printf("Usage : ./a.out <filename>\n");
		return -1;
	}
	
	mkfifo("myfifo", 0664);

	int fdfifo = open("myfifo", O_RDONLY);
	if (fdfifo < 0)
	{
		perror("open fifo error");
		return -1;
	}
	
	int fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0664);
	if (fd < 0)
	{
		perror("open error");
		return -1;
	}

	ssize_t ret = 0;
	char buff[1024] = {0};
	while ((ret = read(fdfifo, buff, sizeof(buff))) > 0)
	{
		write(fd, buff, ret);
	}
	close(fd);
	close(fdfifo);

	return 0;
}

三、对比

项目 无名管道 pipe 有名管道 fifo
通信范围 仅限父子 / 兄弟(有血缘)进程 任意两个无血缘进程
磁盘文件 无文件,内核缓冲区 存在管道文件标识
打开方式 pipe 一次性创建读写两端 必须先用 mkfifo 创建,再 open 打开
相关推荐
NJCloud25 分钟前
OpenStack(四)——进阶篇之私有网络配置与块存储服务(Cinder)部署
linux·网络·云计算·openstack
隔窗听雨眠27 分钟前
TDengine在AIOps中的硬核实战:从时序数据存储到智能运维的完整落地路径
大数据·运维·tdengine
2601_9621282628 分钟前
Nginx知识详解(理论+实战更易懂)
运维·nginx
raindayinrain29 分钟前
深入理解Linux内核--ext文件系统,open/write/read/close执行流程,性能优化
linux·性能优化·文件系统·文件系统调用
Elastic 中国社区官方博客35 分钟前
从建议到修复的 4 个阶段:使用 Elastic Workflows 实现人在回路中的自动化
运维·数据库·人工智能·后端·elasticsearch·ai·自动化
jing.wang_20251 小时前
制作一个带用户操作界面的精简Linux操作系统
linux·服务器
段帅星1 小时前
Mac + iTerm2 运维终端配置参考
运维
智恒百亿1 小时前
从开箱到跑通大模型:一台 RTX 5090 八卡服务器的部署实录(附踩坑清单)
运维·服务器
沫璃染墨1 小时前
《从零入门Linux系统篇(三十二):文件篇·五——深入理解磁盘:从物理结构到LBA寻址》
linux·运维·服务器·系统架构·硬件架构