【进程间通信】管道

news2024/12/27 10:34:34
  • (꒪ꇴ꒪ ),Hello我是祐言QAQ
  • 我的博客主页:C/C++语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍
  • 快上🚘,一起学习,让我们成为一个强大的攻城狮!
  • 送给自己和读者的一句鸡汤🤔:集中起来的意志可以击穿顽石!
  • 作者水平很有限,如果发现错误,请在评论区指正,感谢🙏

           进程间通信(IPC)是操作系统中用于不同进程之间交换数据和信息的一种机制。

        当谈到进程间通信时,管道(Pipe)是一种常见且简单的通信方式。管道主要用于在两个相关进程之间传递数据一个进程充当数据的发送者,而另一个进程充当数据的接收者。它也可以在父进程和子进程之间创建,或者在同一台机器上的不同进程之间创建。数据在管道中按照先进先出的顺序进行传递。

        管道有两种类型:无名管道(Anonymous Pipe)和命名管道(Named Pipe,也称为FIFO)。下面就让我们来详细了解一下这两种管道。

一、无名管道(Anonymous Pipe)

1.概述

        无名管道只能用于亲缘进程间(比如父子进程、兄弟进程、祖孙进程等)创建的一种通信方式写入操作不具有写入原子性,因此只能用于一对一的简单通信情形。无名管道只能在创建进程时使用,无法在其他无关进程之间使用。此外还具有以下特点:

        (1)单向性:数据只能从管道的写入端流向读取端;
        (2)缓冲区:无名管道通常具有有限的缓冲区,用于存储在写入时尚未读取的数据;
        (3)阻塞:如果管道已满(写入端写入速度过快),写入操作可能会阻塞,直到有足够的空间来容纳数据;
        (4)关闭:一旦所有引用管道的进程都关闭了管道,数据就会被清空;

        (5)没有名字,因此无法使用 open( )函数,也不能使用 lseek( )函数来定位。

还需要注意的是:无名管道的读写端是固定的。

        fd[0] :读端,只能进行读操作;

        fd[1]:写端,只能进行写操作。

2.创建无名管道

#include <unistd.h>
int pipe(int pipefd[2]);
参数:pipefd 一个至少具有 2 个 int 型数据的数组,用来存放 PIPE 的读写端描述符

3.示例

        创建一个子进程,实现键盘输入要发送的消息,父进程给子进程发消息,并且能够循环发送,子进程接收消息并显示。

        代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
	// 创建一条无名管道
	int fd[2];
	int p = pipe(fd);  // 创建管道,fd[0] 是读端,fd[1] 是写端
	if (p == -1)
	{
		perror("pipe fail");  // 输出错误信息
		return -1;
	}
	pid_t x = fork();  // 创建子进程
	
	if (x > 0)  // 父进程
	{
		char w_buf[100];
		while(1)  // 无限循环
		{
			printf("请输入要传输的字符串:");
			scanf("%s", w_buf);  // 从用户输入中读取字符串
			write(fd[1], w_buf, strlen(w_buf)+1);  // 将字符串写入管道的写端
			sleep(1);  // 等待1秒钟
		}
	}
	if (x == 0)  // 子进程
	{
		char r_buf[100];
		while(1)  // 无限循环
		{
			bzero(r_buf, sizeof(r_buf));  // 清空读缓冲区
			read(fd[0], r_buf, sizeof(r_buf));  // 从管道的读端读取数据到缓冲区
			printf("father say:%s\n", r_buf);  // 打印读取的数据
		}
	}

	return 0;
}

二、命名管道(Named Pipe / FIFO)

1.概述

         命名管道是一种更为通用的管道,又称有名管道,可以用于没有亲缘关系的进程之间的通信。与无名管道不同,命名管道可以在系统中存在一段时间,允许多个进程随时通过使用相同的管道名称来进行通信。其特点包括:

        (1)可命名性:通过指定一个唯一的名称(FIFO),进程可以随时打开并使用管道;
        (2)跨进程通信:不同进程可以通过打开相同名称的管道来进行通信;
        (3)非阻塞:命名管道通常可以设置为非阻塞模式,即使管道未就绪,进程仍然可以继续运行;
        (4)持久性:命名管道会保留在文件系统中,直到被明确删除;

        (5)跟普通文件一样可使用统一的 read( )/write( )来读写。任何具有相应权限的进程都可以使用 open( )函数来获取 FIFO 的文件描述符。

2.创建命名管道

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
    pathname FIFO 的文件名
    mode 文件权限

3.示例

        设计两个进程,进程间通过有名管道进行通信,两个进程都既可以写数据,也可读数据,当写入的数据是“quit”的时候,写进程关闭;读进程收到“quit”,也关闭。

         完整代码:

jack.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#define PATHFIFO1 "/tmp/myfifo1"
#define PATHFIFO2 "/tmp/myfifo2"

int main(int argc, char const *argv[])
{
	//创建有名管道,如果已经存在,不创建就行了
	if (access(PATHFIFO1, F_OK))
	{
		int fifo = mkfifo(PATHFIFO1, 0777);
		if (fifo == -1)
		{
			perror("mkfifo");
			return -1;
		}
	}
	if (access(PATHFIFO2, F_OK))
	{
		int fifo = mkfifo(PATHFIFO2, 0777);
		if (fifo == -1)
		{
			perror("mkfifo");
			return -1;
		}
	}

	//打开有名管道
	int fifo_fd1 = open(PATHFIFO1, O_RDWR);
	if (fifo_fd1 == -1)
	{
		perror("open");
		return -1;
	}
	int fifo_fd2 = open(PATHFIFO2, O_RDWR);
	if (fifo_fd2 == -1)
	{
		perror("open");
		return -1;
	}

	pid_t x = fork();
	if (x > 0)
	{
		char w_buf[100];
		while(1)
		{
			scanf("%[^\n]", w_buf);//%[^\n]:除了'\n'这个字符,我全都要,慎用   %s:接收字符串的时候,不会接收空格
			while(getchar()!='\n');
			//发送数据
			write(fifo_fd1, w_buf, strlen(w_buf));
			if (strncmp(w_buf, "quit", 4) == 0)
			{
				exit(0);
			}
		}
	}
	if (x == 0)
	{
		char r_buf[100];
		while(1)
		{
			bzero(r_buf, sizeof(r_buf));
			read(fifo_fd2, r_buf, sizeof(r_buf));
			printf("rose:%s\n", r_buf);
			if (strncmp(r_buf, "quit", 4) == 0)
			{
				//自己发个信号杀死父进程
				return -1;
			}
		}
	}
	
	return 0;
}

rose.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#define PATHFIFO1 "/tmp/myfifo1"
#define PATHFIFO2 "/tmp/myfifo2"

int main(int argc, char const *argv[])
{
	//创建有名管道,如果已经存在,不创建就行了
	if (access(PATHFIFO1, F_OK))
	{
		int fifo = mkfifo(PATHFIFO1, 0777);
		if (fifo == -1)
		{
			perror("mkfifo");
			return -1;
		}
	}
	if (access(PATHFIFO2, F_OK))
	{
		int fifo = mkfifo(PATHFIFO2, 0777);
		if (fifo == -1)
		{
			perror("mkfifo");
			return -1;
		}
	}

	//打开有名管道
	int fifo_fd1 = open(PATHFIFO1, O_RDWR);
	if (fifo_fd1 == -1)
	{
		perror("open");
		return -1;
	}
	int fifo_fd2 = open(PATHFIFO2, O_RDWR);
	if (fifo_fd2 == -1)
	{
		perror("open");
		return -1;
	}

	pid_t x = fork();
	if (x > 0)
	{
		char w_buf[100];
		while(1)
		{
			scanf("%[^\n]", w_buf);//%[^\n]:除了'\n'这个字符,我全都要,慎用   %s:接收字符串的时候,不会接收空格
			while(getchar()!='\n');
			//发送数据
			write(fifo_fd2, w_buf, strlen(w_buf));
			if (strncmp(w_buf, "quit", 4) == 0)
			{
				exit(0);
			}
		}
	}
	if (x == 0)
	{
		char r_buf[100];
		while(1)
		{
			bzero(r_buf, sizeof(r_buf));
			read(fifo_fd1, r_buf, sizeof(r_buf));
			printf("jack:%s\n", r_buf);
			if (strncmp(r_buf, "quit", 4) == 0)
			{
				//自己发个信号杀死父进程
				return -1;;
			}
		}
	}

	return 0;
}

三、总结

        总的来说,管道是一种简单且有效的进程间通信方式,特别适合在具有亲缘关系的进程之间传递数据。但对于更复杂的通信需求,可能需要考虑其他IPC方式。常见的应用呢就是写入日志文件,我们该问题进行一个简化就可得到如下的图示:

        完成这个任务程序也很简单,我们需要一个总的日志文件保存程序和五个发送日子程序,这样我们就能模拟出日志文件的产出和写入,像这样:

        此外,在使用管道进行进程间通信需要注意以下几点:

        (1) 管道是基于字节流的,因此数据传输不会被分段;
        (2)管道的写入和读取操作是阻塞的,可能需要额外的机制来处理阻塞情况;
        (3) 管道通常用于在具有亲缘关系的进程(例如父子进程)之间进行通信,或者在需要共享数据的进程之间进行通信。

        更多C/C++语言Linux系统数据结构ARM板实战相关文章,关注专栏:

   手撕C语言

            玩转linux

                    脚踢数据结构

                            系统、网络编程

                                     探索C++

                                             6818(ARM)开发板实战

📢写在最后

  • 今天的分享就到这啦~
  • 觉得博主写的还不错的烦劳 一键三连喔~
  • 🎉🎉🎉感谢关注🎉🎉🎉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/954057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于RabbitMQ的模拟消息队列需求文档

文章目录 一、项目背景二、需求分析1.核心概念2.BrokerServer核心组件3.核心API4.交换机类型5.持久化6.网络通信7.消息应答 三、消息队列模块划分 一、项目背景 什么是消息队列&#xff1f; 消息队列就是&#xff0c;基于阻塞队列&#xff0c;封装成一个独立的服务器程序&#…

springboot实战(一)之项目搭建

环境准备 ideajdk1.8springboot版本 2.7.15 项目开始 1.打开idea&#xff0c;点击new project 2.选择spring initillizr 核对&#xff1a;Server Url是否是&#xff1a;start.spring.io&#xff0c;然后根据自己依次设置项目名称、存储位置和包名&#xff0c;如下&#xff…

洛朗展式求留数方法计算超越函数f(x)=e^(cosx)的定积分

https://math.stackexchange.com/questions/2468863/what-is-the-integral-of-e-cos-x https://tieba.baidu.com/p/6881253594 例子 https://www.zhihu.com/question/441124046/answer/1697123609?utm_id0

《Kubernets证书篇:kubernetes1.24.17证书修改时间限制》

一、背景 Kubernetes 默认的证书有效期只有1年,因此需要每年手动更新一次节点上面的证书,特别麻烦而且更新过程中可能会出现问题,因此我们要对 Kubernetes 的 SSL 证书有效期进行修改,这里将证书的时间限制修改为100年。 环境信息如下: 操作系统内核版本K8S版本Ubuntu 20.…

百度的AI画图和讯飞的AI画图目前就这样了

今天接到百度的短信&#xff0c;说给我一个搜索AI的测试权限&#xff0c;心血来潮&#xff0c;让AI给我做个画&#xff0c;百度和讯飞来做个PK&#xff0c;结果都不敢恭维哈&#xff0c;双方都有待提高&#xff0c;加油&#xff01;

代码解读 FCOS网络(基于mmrotate框架)

文章目录 1. rotated_fcos_head.py1.1 __init__函数1.2 forward_single 函数1.2.1 父类的forward_single 函数&#xff08;anchor_free_head.py&#xff09;1.2.2 _init_layers 函数 1.3 loss 函数1.3.1 get_targets 函数1.3.1.1 _get_target_single 函数 1.3.2 prior_generato…

vr城乡规划建筑设计元宇宙平台提高工作协同效率

随着科技的发展&#xff0c;元宇宙正逐渐渗透到各个行业领域中&#xff0c;特别是建筑设计&#xff0c;那么在建筑设计中&#xff0c;利用元宇宙平台有哪些特点及优势呢? 面对建筑行业日益突出的高消耗、高风险、高投入、低利润的问题&#xff0c;如何提升企业经营管理水平&am…

Python Pyecharts 制图

基本图表 - pyecharts - A Python Echarts Plotting Library built with love. from pyecharts import options as opts from pyecharts.charts import Pie from pyecharts.faker import Fakerc (Pie().add("",[list(z) for z in zip(["7室1厅", "5…

基于KNN算法的鸢尾花种类预测

K近邻法算法思想 K近邻法&#xff08;K-Nearest Neighbor&#xff0c;KNN&#xff09;是一种基本的分类和回归方法&#xff0c;是监督学习方法里的一种常用方法。K近邻算法用一句通俗的古语来说就是&#xff1a;“物以类聚&#xff0c;人以群分”。有人说看一个人什么样&#…

【Unity3D游戏魔坦之争】游戏结束流程封装实现【七】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

虹科分享 | 温度边缘效应对冻干成品含水量的影响(下)——优化和总结

上一篇文章中介绍到借助虹科Ellab的温度记录仪观察到由于冻干机壁面温度的影响&#xff0c;形成的边缘效应导致同一隔板的不同区域冻干饼块的干燥程度不均匀&#xff0c;含水量不同。 06 初次试验结果&#xff1a; 二次干燥中的产品温度显示&#xff1a; 放置在搁板中间的产品…

Linux内核源码分析 (A)常见内核面试题

Linux内核源码分析 (A)常见内核面试题 文章目录 Linux内核源码分析 (A)常见内核面试题调用 schedule() 进行进程切换的方式有几种CFS调度器vruntime的计算方式 网站收集面试题集合1 调用 schedule() 进行进程切换的方式有几种 系统调用 do_fork()&#xff1a;copy_process()定…

供热管网安全运行监测,提升供热管网安全性能

城市管网是城市的“生命线”之一&#xff0c;是城市赖以生存和发展的基础&#xff0c;在城市基础设施高质量发展中发挥着重要作用。供热管网作为城市生命线中连接供热管线与热用户的桥梁&#xff0c;担负着向企业和居民用户直接供热的重要职责。随着城市热力需求的急剧增加&…

【SpringBoot】Swagger和knife4j的使用

文章目录 前言1.什么是Swagger和Knife4j2.Swagger和Knife4j怎么用2.1 引入依赖2.2 设置配置类2.3 启动验证 3.完结撒花 前言 springboot笔记集合: springboot笔记合计 没用的废话理论不多说&#xff0c;会用就完了 1.什么是Swagger和Knife4j Swagger是一种开源的API描述语言…

ranger无法同步用户问题解决

1.首先就是定位日志,日志目录 cd /var/log/ranger/usersync 定位到问题报错如下: LdapDeltaUserGroupBuilder.getUsers() failed with exception:java.naming.AuthticationExceptiom :[LDAP:error code 49 - Invalid Credentials]:remaing name ‘ouPeople,dc*.dccom’ 解决办法…

frida动态调试入门01——定位关键代码

说明 frida是一款Python工具可以方便对内存进行hook修改代码逻辑在移动端安全和逆向过程中常用到。 实战 嘟嘟牛登录页面hook 使用到的工具 1&#xff0c;jadx-gui 2&#xff0c;frida 定位关键代码 使用jadx-gui 进行模糊搜索&#xff0c;例如搜索encyrpt之类的加密关键…

Python Qt(八)Treeview

源代码&#xff1a; # -*- coding: utf-8 -*-# Form implementation generated from reading ui file qt_treeview.ui # # Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not…

vnc与windows之间的复制粘贴

【原创】VNC怎么和宿主机共享粘贴板 假设目标主机是linux&#xff0c;终端主机是windows&#xff08;就是在windows上使用VNC登陆linux&#xff09; 在linux中执行 vncconfig -nowin& 在linux选中文字后&#xff0c;无需其他按键&#xff0c;直接在windows中可以黏贴。 …

2023-8-31 Dijkstra求最短路(二)

题目链接&#xff1a;Dijkstra求最短路 II #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <queue>using namespace std;typedef pair<int, int> PII;const int N 150010;int n, m; int h[N…

利用GeoServer进行跨图层空间查询

Cross-layer filtering 跨层过滤提供了从层A中查找与层B中的特征具有特定关系的特征的能力。例如&#xff0c;这可以用于查找距离指定商店给定距离内的所有公交车站&#xff0c;或者查找指定城区内的所有咖啡店。 querylayer模块添加了实现跨层过滤的过滤功能。这些功能通过查…