Linux进程间的通信(二)管道通信及其实际应用(主要是实际编程应用,底层涉及不太多,想了解底层参考《UNIX环境高级编程》)

本文主要是介绍Linux进程间的通信(二)管道通信及其实际应用(主要是实际编程应用,底层涉及不太多,想了解底层参考《UNIX环境高级编程》),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

简单介绍一下管道的概念及其特性

命名管道

命名管道例程

匿名管道

 匿名管道例程

 Linux管道通信实战演示

1、利用管道建立聊天室,实现两个用户间的发送和接受消息

 2、利用管道进行文件的传输


简单介绍一下管道的概念及其特性

管道是一种进程间通信(IPC)机制,它允许一个进程将数据传递给另一个进程。管道文件可以看作是一个临时的、基于内存的数据通道,数据在其中以先进先出(FIFO)的方式流动。

例如,假设有两个程序 A 和 B,程序 A 产生一些数据,而程序 B 需要使用这些数据。通过创建一个管道,程序 A 可以将数据写入管道,而程序 B 可以从管道中读取这些数据,从而实现了两个程序之间的数据传递。

如果read读取完管道里的数据,管道为空,就会读阻塞

如果write写入的数据大于管道的容量,管道容量满了,就会写阻塞

如果所有指向管道写端的文件描述符都关闭了,而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样

如果所有指向管道读端的文件描述符都关闭了,这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。

参考文献:Linux 的进程间通信:管道 - 知乎 (zhihu.com)

参考文献:linux之《管道》_管道文件-CSDN博客

命名管道

命名管道是真实存在在磁盘中的!

命名管道文件的创建

Linux命令行创建管道文件
mkfifo  <pipename>

函数原型:

#include <sys/types.h>
#include <sys/stat.h>

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

pathname:要创建的管道文件名字。
mode:用来规定FIFO的读写权限。

命名管道例程

创建一个命名管道实现两个进程间的通信:

write.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{// 1.打开管道文件int fd = open("/home/loading/pipetest", O_RDWR);if (fd < 0){perror("打开管道失败\n");return -1;}// 2.写入数据到管道中while (1){printf("请输入写入管道的数据\n");char buf[1024] = {0};scanf("%s", buf);write(fd, buf, strlen(buf));}
}

read.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{// 1.打开管道文件int fd = open("/home/loading/pipetest", O_RDWR);if (fd < 0){perror("打开管道失败\n");return -1;}// 2.读取管道数据while (1){char buf[1024] = {0};read(fd, buf, 1024);printf("读取到的管道数据 %s\n", buf);}
}

 在运行前需要现在open的路径下mkfifo一个pipe文件,我这里的路径是/home/loading/,根据实际创建

运行时需要打开两个Linux终端,好验证进程间的通信。

匿名管道

没有文件名,在内存中以特殊的文件描述符对的形式存在。

当一个进程调用 pipe 系统调用时,内核会创建一个匿名管道,并返回两个文件描述符,一个用于读操作,一个用于写操作。

通常在父进程中创建管道后,再通过 fork 创建子进程。此时,子进程会继承父进程的文件描述符表,从而父子进程都可以访问这个管道。

匿名管道本身并不占用磁盘或者其他外部存储的空间,在Linux的实现上,它占用的是内存空间。所以,Linux上的匿名管道就是一个操作方式为文件的内存缓冲区。

函数原型
#include <unistd.h>
int pipe(int filedes[2]);

pipefd[0]:管道数据读取端,读取时必须关闭写入端,即close(pipefd[1])
pipefd[1]:管道数据写入端,写入时必须关闭读取端,即close(pipefd[0])

返回值:成功   0  
失败   -1

由于基于fork机制,所以匿名管道只能用于父进程和子进程之间,或者拥有相同祖先的两个子进程之间 (有亲缘关系的进程之间)!

参考文献:Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal) - as_ - 博客园 (cnblogs.com)

 匿名管道例程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>int main()
{// 1.创建一个匿名管道int pipefd[2] = {0};int ret = pipe(pipefd);if (ret < 0){perror("创建匿名管道失败\n");return -1;}// 2.创建父子进程pid_t pid = fork();if (pid == 0) // 子进程{while (1) // 子进程写入管道{char buf[1024] = {0};printf("请输入想要写入管道的数据\n");scanf("%s", buf);write(pipefd[1], buf, strlen(buf));usleep(50000);	//延时半秒}}if (pid > 0) // 父进程{while (1) // 父进程读管道{char buf[1024] = {0};read(pipefd[0], buf, 1024);printf("读取到的管道数据 %s\n", buf);}}
}
 Linux管道通信实战演示
1、利用管道建立聊天室,实现两个用户间的发送和接受消息

      原理:需要建立两个命名管道实现收发

client1.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{// 1.打开一个管道文件int fd1 = open("/home/loading/pipe1", O_RDWR);int fd2 = open("/home/loading/pipe2", O_RDWR);if (fd1 < 0){perror("打开管道失败\n");return -1;}if (fd2 < 0){perror("打开管道失败\n");return -1;}pid_t pid1 = fork();// 写数据if (pid1 == 0){while (1){char buf1[1024] = {0};printf("请输入要发送的数据\n");scanf("%s", buf1);// lseek(fd,0,SEEK_SET);write(fd2, buf1, strlen(buf1));}}// 读数据if (pid1 > 0){while (1){char buf[1024] = {0};// lseek(fd,0,SEEK_SET);无论命名还是匿名管道,它的文件描述都没有偏移量的概念,所以不能用lseek进行偏移量调整read(fd1, buf, 1024); // 管道没有数据,则会阻塞等待printf("管道1读取数据 %s\n", buf);}}
}

 client2.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{// 1.打开一个管道文件int fd1 = open("/home/loading/pipe1", O_RDWR);// 1.打开一个管道文件int fd2 = open("/home/loading/pipe2", O_RDWR);if (fd1 < 0){perror("打开管道失败\n");return -1;}if (fd2 < 0){perror("打开管道失败\n");return -1;}pid_t pid =fork();//写数据if(pid==0){while (1){printf("请输入要发送的数据\n");char buf[1024]={0};scanf("%s", buf);// lseek(fd,0,SEEK_SET);write(fd1, buf, strlen(buf));}}//读数据if(pid>0){while (1){char buf1[1024]={0};// lseek(fd1,0,SEEK_SET);read(fd2, buf1, 1024);// 管道没有数据,则会阻塞等待printf("管道2读取数据 %s\n", buf1);}}}
 2、利用管道进行文件的传输

 send.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>// 获取文件的大小
int get_file_size(char *file)
{// 1.打开文件int fd = open(file, O_RDWR);if (fd < 0){perror("");return -1;}// 2.偏移光标到文件末尾int file_size = lseek(fd, 0, SEEK_END);close(fd);return file_size;
}// 发送进程
int main(int argc, char *argv[])
{if (argc != 2){printf("请输入需要发送的文件名\n");return 0;}// 1.打开发送的文件int fd = open(argv[1], O_RDWR);if (fd < 0){perror("");return -1;}// 2.打开管道文件int pipe_fd = open("/home/loading/pipe", O_RDWR);if (pipe_fd < 0){perror("");return -1;}// 3.发送文件大小给接收端int file_size = get_file_size(argv[1]);char head[1024] = {0};sprintf(head, "%d", file_size);write(pipe_fd, head, strlen(head));// 休眠等待客户端应答sleep(2);char ack[50] = {0};read(pipe_fd, ack, 50);if (strcmp(ack, "no") == 0){printf("客户端拒绝接收\n");return -1;}while (1){char buf[1024 * 10] = {0};// 读取文件的数据int size = read(fd, buf, 1024 * 10);if (size <= 0){break;}// 写入到管道文件中write(pipe_fd, buf, size); // 读取到多少就写入多少}printf("发送完毕\n");close(fd);close(pipe_fd);
}

 recv.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>// 接收进程
int main(int argc, char *argv[])
{if (argc != 2){printf("请输入需要接收的文件名\n");return 0;}// 1.打开发送的文件int fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777);if (fd < 0){perror("");return -1;}// 2.打开管道文件int pipe_fd = open("/home/loading/pipe", O_RDWR);if (pipe_fd < 0){perror("");return -1;}// 3.接收文件的大小char head[1024] = {0};read(pipe_fd, head, 1024);int filesize = atoi(head);printf("客户端发送文件,大小为%d,1.yes or 2.no\n", filesize);int n = 0;scanf("%d", &n);if (n == 1){write(pipe_fd, "yes", 3);}else{write(pipe_fd, "no", 2);return -1; // 退出接收}// 休眠等待sleep(1);int down_size = 0; // 下载大小while (1){char buf[1024 * 10] = {0};// 读取管道数据int size = read(pipe_fd, buf, 1024 * 10);if (size <= 0){break;}// 写入到本地文件中write(fd, buf, size); // 读取到多少就写入多少down_size += size;printf("当前接收文件的大小:%d ,进度%.2f %%\r", down_size, (float)down_size * 100 / filesize);if (down_size >= filesize){printf("接收完毕\n");break;}}printf("接收完毕\n");close(fd);close(pipe_fd);
}

这篇关于Linux进程间的通信(二)管道通信及其实际应用(主要是实际编程应用,底层涉及不太多,想了解底层参考《UNIX环境高级编程》)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1119933

相关文章

linux hostname设置全过程

《linuxhostname设置全过程》:本文主要介绍linuxhostname设置全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录查询hostname设置步骤其它相关点hostid/etc/hostsEDChina编程A工具license破解注意事项总结以RHE

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Java MQTT实战应用

《JavaMQTT实战应用》本文详解MQTT协议,涵盖其发布/订阅机制、低功耗高效特性、三种服务质量等级(QoS0/1/2),以及客户端、代理、主题的核心概念,最后提供Linux部署教程、Sprin... 目录一、MQTT协议二、MQTT优点三、三种服务质量等级四、客户端、代理、主题1. 客户端(Clien

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素