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 打开
相关推荐
wdfk_prog28 分钟前
ROS教程10:从 gtest 到 rostest——Unit Test、Node 集成测试、rosbag 与 rqt 验证闭环
运维·缓存·docker·容器·ros
吴声子夜歌1 小时前
Shell编程实例——高级脚本编程(一)
linux·运维·网络·shell
Huangjin007_2 小时前
【Linux 系统篇(二十一)】进程(九):进程等待 wait/waitpid、status状态参数
linux·运维·服务器
剑指offer.2 小时前
嵌入式硬件-ARM芯片的启动
c语言·嵌入式硬件·嵌入式
螺蛳粉 螺蛳粉2 小时前
mysql的备份与恢复
linux·运维·数据库·mysql
ao-weilai2 小时前
Linux网络编程:Socket UDP
linux·服务器·网络·c++
可乐鸡翅yeah_2 小时前
混合内容 Mixed‑Content 安全策略,HLS HTTPS 页面加载 HTTP 资源排错实战
运维·ffmpeg·音视频·媒体·m3u8
爱吃香菜的初学者3 小时前
九.Linux——文件操作
linux
园长的牧歌3 小时前
Ubuntu 打开应用在导航栏没有图标是个齿轮
linux·ubuntu
曦夜日长3 小时前
Linux系统篇,进程概念(四):内核链表的深度理解、进程优先级的底层理解
linux·运维·服务器·php