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 打开
相关推荐
志栋智能4 小时前
运维超自动化的版本控制与回滚机制
运维·自动化
中云DDoS CC防护蔡蔡4 小时前
DDOS和CC攻击谁更可怕
运维·服务器·经验分享·网络安全·ddos
Ruiery5 小时前
Linux 6.6内核内存管理深度解析(十六):mmap — 用户态“申请内存“的真相
linux·运维·服务器
ClickHouseDB5 小时前
ClickHouse Cloud 与 Snowflake 实时性价比对比:CostBench 测试中的 412 倍差距来
运维·数据库·系统架构
欧米欧5 小时前
Linux系统篇(七),环境变量的底层溯源,核心获取机制与安全应用
linux·运维·服务器
Ruiery5 小时前
Linux 6.6内核内存管理深度解析(十八):KASLR 内核地址空间随机化
linux·运维·服务器·安全
todoitbo6 小时前
群晖NAS部署JmalCloud:Docker安装、文件管理、博客与公网访问完整教程
运维·docker·容器
时间售卖铺6 小时前
触发器与函数实战:MySQL高效编程秘诀
运维·数据库·mysql·云原生
qetfw6 小时前
UOS MariaDB 数据库:安装、最小权限与备份验证
linux·数据库·mariadb·uos
前端精髓6 小时前
调用大模型API
运维·服务器·microsoft