Linux:IPC - 管道

Linux:IPC - 管道

    • 管道原理
    • 匿名管道
    • 管道读写机制
    • 管道特性
    • 命名管道
      • mkfifo指令
      • mkfifo接口


进程间通信的目的,是为了让两个进程看到同一份资源,在Linux中,主要的进程间通信有管道system VPOSIX,本博客讲解管道。

管道原理

在这里插入图片描述

以上是一个进程,它打开了一些文件,于是在进程PCB中的struct file_struct成员中,就会指向当前进程已经打开的文件的结构体struct file

那么假设该进程现在创建了一个子进程:

在这里插入图片描述

由于子进程会继承父进程的PCB,同时也会继承父进程的struct files_struct,相当于同时打开了一份文件。那么现在父子进程就都可以看到同一份文件了!

以上就是管道通信的基本原理:让进程之间发生PCB的继承,从而通过PCB打开同一份文件

现在来看看管道是如何实现的:

  1. 父进程创建管道

在这里插入图片描述

一开始父进程先同时打开一个文件的读端和写端,此时读端和写端分别被一个父进程的fd管理。

  1. 父进程创建子进程

在这里插入图片描述

当父进程创建完管道后,此时创建出子进程,由于子进程会继承父进程的PCB,也会继承父进程的fd,此时父子进程都打开了管道的读端和写端。

  1. 父子进程分别关掉管道的读端或写端

在这里插入图片描述

管道的通信是单向的,必须是一端读,一端写

    1. 父进程关闭读端,子进程关闭写端,此时父进程向管道写入,子进程从管道读取
    1. 父进程关闭写端,子进程关闭读端,此时子进程向管道写入,父进程从管道读取

上图中,父进程保留了写端,子进程保留了读端,就是父进程向管道写入,子进程从管道读取。现在父子进程之间就可以通信了。

管道分为两种,匿名管道命名管道


匿名管道

以上说的只是管道的基本原理,但是操作系统中的管道会有很多额外优化。

操作系统进行管道通信时,不会在磁盘上创造文件,而是在内存中临时开辟一个缓冲区

想要在系统中创建管道,需要调用系统调用接口pipe,其包含在头文件<unistd.h>中,函数原型如下:

int pipe(int pipefd[2]);

参数int pipefd[2]是一个输出型参数,其本质是一个长度为2的数组,其中fd[0]是管道的读端fd,而fd[1]是管道的写端fd

返回值:

  • 返回 0 表示开辟管道成功
  • 返回 -1 表示开辟管道失败,并且会设置错误码

先写两个readerwriter函数:

void reader(int rfd)
{char buffer[1024];while (true){read(rfd, buffer, sizeof(buffer));cout << "get massage: " << buffer << endl;}
}

参数rfd是读端fd,通过这个fd从管道读取。reader函数会从管道中读取内容到buffer中,随后输出buffer的内容。

void writer(int wfd)
{char buffer[128];int count = 0;while(true){snprintf(buffer, sizeof(buffer), "pid = %d, count = %d", getpid(), count++);write(wfd, buffer, strlen(buffer));sleep(1);}
}

参数wfd是写端fd,写端每隔一秒向管道写入buffer的内容。

主函数:

int main()
{int pipefd[2];pipe(pipefd);int rfd = pipefd[0];int wfd = pipefd[1];pid_t id = fork();if(id == 0){close(rfd);writer(wfd);exit(0);}close(wfd);reader(rfd);waitpid(id, NULL, 0);return 0;
}

通过pipe创建了管道,此时读端rfd就是pipe[0],写端wfd就是pipe[1]。随后通过fork创建子进程,子进程关闭rfd,进行写入,父进程关闭wfd,进行读取。

输出结果:

在这里插入图片描述

此时父进程成功通过writer函数读取到了管道的内容。


管道读写机制

当管道内没有数据,读端会一直阻塞等待,直到管道中出现数据

刚刚我们的reader函数如下:

void reader(int rfd)
{char buffer[1024];while (true){read(rfd, buffer, sizeof(buffer));cout << "get massage: " << buffer << endl;}
}

你会发现,这个while循环中,没有sleep这样的语句,但是刚刚输出结果中,依然是每隔一秒输出一条语句。这是因为在read读取管道数据的时候,如果管道中没有内容,就进行阻塞等待,直到管道出现数据。而写端每隔一秒写入,也就是说管道中每隔一秒才有数据,所以读端每隔一秒才能read到数据。

写端一直写,读端不读取数据,当管道被写满的时候,读端就会阻塞等待

现在修改写端函数writer如下:

void writer(int wfd)
{char buffer[128];int count = 0;while(true){write(wfd, "c", 1);cout << count++ << endl;}
}

写端一直向管道写入字符"c",并且利用count计数。但是此时读端不读取,也就是不调用reader函数。

输出结果:
在这里插入图片描述

最后输出到count = 65535的样子,就不再写入数据了,此时写端停止写入数据,因为管道满了。我们也可以大概看出来,Ubuntu的管道大小约为65536 byte,也就是64 kb的样子。

如果读写端关闭了管道,会发生什么?

当写端关闭管道,此时读端依然可以读取管道,但是read接收到返回值为`0

写端writer如下:

void writer(int wfd)
{int count = 0;while(true){write(wfd, "c", 1);count++;if(count == 1024)break;}close(wfd);cout << "writer close..." << endl;
}

写端写入1024个字符后,终止循环,随后close关闭掉写端。

读端reader如下:

void reader(int rfd)
{char buffer[1024];sleep(5);while (true){int n = read(rfd, buffer, sizeof(buffer));if(n != 0)cout << "get massage: " << buffer << endl;elsecout << "n = 0" << endl;sleep(1);}
}

一开始reader睡眠五秒,等待writer写入数据,随后通过read读取管道,并接收返回值给n

输出结果:

在这里插入图片描述

五秒后,写端关闭了管道,此时读端依然可以读取所有的数据,把1024个字符读取走后,随后的所有读取都是n = 0

因此可以证明,写端关闭管道时,读端依然可以正常读取,但是当管道读完后,read返回值会变成0

当读端关闭管道,此时写端进程直接终止

写端writer如下:

void writer(int wfd)
{int count = 0;while(true){write(wfd, "c", 1);cout << count++ << endl;sleep(1);}
}

子进程写端每隔一秒写入一个字符"c"

读端reader如下:

void reader(int rfd)
{sleep(5);close(rfd);cout << "reader close..." << endl;
}

父进程读端五秒后,通过close关闭读端。

而在main函数中,父进程通过waitpid等待子进程,随后输出子进程的瑞退出信号:

int status;
waitpid(id, &status, 0);
cout << "Sig = " << (status & 0X7F) << endl; 

输出结果:

在这里插入图片描述

父进程是读端,子进程是写端,子进程一直死循环写入字符,父进程五秒后关闭读端。

可以发现,按理来说,子进程应该一直处于死循环中进行写入,所以waitpid函数应该一直阻塞等待才对。可是最后子进程只写入了五个字符,就被强制终止了,并且waitpid成功了。

这是因为父进程作为读端,五秒后关闭了读端,此时写端也会被强制终止。终止信号为13号信号SIGPIPE

总结一下四个读写端的机制:

  1. 当管道内没有数据,读端会一直阻塞等待,直到管道中出现数据
  2. 写端一直写,读端不读取数据,当管道被写满的时候,读端就会阻塞等待
  3. 当写端关闭管道,此时读端依然可以读取管道,但是read接收到返回值为`0
  4. 当读端关闭管道,此时写端进程直接终止

管道特性

通过以上案例,我们再来总结一下管道有哪些特性:

  • 管道带有自同步机制:管道中,只有写了数据,读端才会读取,否则阻塞。而如果管道被写满了,读端不读取,那么写端停止写入。

简单来说就是,只有数据被读取走了,才会写入。只有数据被写入了,才会读取。

  • 匿名管道用于带有血缘关系的进程通信:管道是通过继承来打开同一份管道文件,因此只有多个进程之间有血缘关系,才能通过管道通信(这个特性只针对匿名管道,后续讲解的命名管道可以在无血缘关系的进程之间通信

  • 管道具有面向字节流的特性:在刚刚的一个案例中,写端写入了1024个字符,而读端只读取了一次:
    在这里插入图片描述
    这个案例中,读端可以一次性把所有的字符都读取走,这个特性叫做面向字节流

  • 管道生命周期和进程一致:当进程退出时,管道会自动释放

  • 管道只能单向通信:通过管道通信的双方,都只能保留一个端口进行通信,因此管道通信是单向的


命名管道

先前我们创建的管道是匿名的,也就是说管道没有自己的名称,只是内存中的一块缓冲区,当进程结束时,管道也会自动释放。

如果说现在硬盘中存在一个有名字的文件,那么是不是就可以让两个进程同时打开这个文件,从而进行通信:

在这里插入图片描述

上图中,这个进程AB都打开了文件log.txt,此时两者就可以通过文件log.txt通信了。这种管道叫做命名管道

mkfifo指令

我们可以通过mkfifo指令来创建一个管道文件:

mkfifo 文件名

示例:

在这里插入图片描述

通过mkfifo log.txt创建了一个管道,通过左侧第一个字符可知,这个文件的类型是p,也就是管道文件

现在向管道中写入内容:

在这里插入图片描述

向管道log.txt中写入了字符串hello,此时命令行陷入了阻塞状态,在等待别人读取这个管道的内容。

我们在另外一个bash中读取这个管道:

在这里插入图片描述

另外一个bash成功读取到了内容,并且原先的阻塞态也终止了。

mkfifo接口

我们也可以在进程中通过系统调用接口来创建管道,mkfifo接口,包含在头文件<sys.types.h><sys/stat.h>中,函数原型如下:

int mkfifo(const char* pathname, mode_t mode)

参数:

  1. pathname:即管道文件创建的路径
  2. mode:管道文件的初始权限

返回值:

  1. 返回0:管道创建成功
  2. 返回 非0:管道创建失败

示例:

int main()
{int n = mkfifo("./log.txt", 0666);if(n == 0)cout << "success" << endl;elsecout << "error" << endl;return 0;
}

通过mkfifo接口,在当前路径下创建了log.txt管道文件,管道的初始权限为0666

输出结果:

在这里插入图片描述

现在当前目录下就出现了log.txt管道文件了。

后续只需要通过在不同进程中通过open接口打开这个文件,就可以进行进程间通信了

如果你想要在进程中销毁这个管道文件,使用unlink接口即可:

unlink("./log.txt");

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

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

相关文章

adb卸载系统垃圾应用

//获取包名 输入如下代码&#xff0c;然后在打开和关闭要获取包名的app就会打印出该app的包名 adb shell am monitor //卸载系统应用 -k会保留用户数据&#xff0c;不包含-k则不会保留用户数据 adb shell pm uninstall -k --user 0 包名 &#xff08;包名一般为&#xff1a;c…

OPPO Reno12 系列正式发布,仅2699元起售

5月23日&#xff0c;OPPO发布科技潮品 Reno12 系列&#xff0c;包含 Reno12 与 Reno12 Pro&#xff0c;以超美小直屏设计&#xff0c;以及行业首发的新科技&#xff0c;引领全新潮流方向。 据「TMT星球」了解&#xff0c;首次亮相的全新配色 Reno12 「千禧银」与Reno12 Pro的「…

【华为】将eNSP导入CRT,并解决不能敲Tab问题

华为】将eNSP导入CRT&#xff0c;并解决不能敲Tab问题 eNSP导入CRT打开eNSP&#xff0c;新建一个拓扑右键启动查看串口号关联CRT成功界面 SecureCRT连接华为模拟器ensp,Tab键不能补全问题选择Options&#xff08;选项&#xff09;-- Global Options &#xff08;全局选项&#…

Java NIO 基础

Java NIO 基础 1. NIO 介绍2. NIO 三大组件2.1 Channel2.1.1 常见的 Channel2.1.2 常用方法 2.2 Buffer2.2.1 常见的 Buffer2.2.2 重要属性2.2.3 常用方法 2.3 Selector2.3.1 四种事件类型 1. NIO 介绍 NIO&#xff08;non-blocking io&#xff09;&#xff1a;非阻塞IO&#…

债务重组全攻略:五大益处、四步流程、三大条件、两类费用

一、债务重组的五大益处 近两年&#xff0c;债务重组业务在市场上崭露头角&#xff0c;许多国企、事业单位以及互联网巨头的员工利用此机会&#xff0c;通过债务重组获取了更多的投资资金。这一趋势不仅帮助众多客户优化了债务结构&#xff0c;还实现了以下几个显著优势&#…

网络空间资产梳理

网络空间资产梳理 网络安全建设的实质是对风险的管理&#xff0c;古人云&#xff1a;知己知彼百战不殆。所谓知己&#xff0c;就是要了解自己的资产以及这些资产的脆弱性&#xff0c;知彼就是了解外部威胁及威胁所使用的手段。要做到知己&#xff0c;首先就要对自身的资产进行梳…

Java 多线程抢红包

问题需求 一个人在群里发了1个100元的红包&#xff0c;被分成了8个&#xff0c;群里有10个人一起来抢红包&#xff0c;有抢到的金额随机分配。 红包功能需要满足哪些具体规则呢? 1、被分的人数抢到的金额之和要等于红包金额&#xff0c;不能多也不能少。 2、每个人至少抢到1元…

海山数据库(He3DB)代理ProxySQL使用详解:(一)架构说明与安装

一、ProxySQL介绍 1.1 简介 业界比较知名的MySQL代理&#xff0c;由ProxySQL LLC公司开发并提供专业的服务支持&#xff0c;基于GPLv3开源协议进行发布,大部分配置项可动态变更。后端的MySQL实例可根据用途配置到不同的hostgroup中&#xff0c;由ProxySQL基于7层网络协议,将来…

linux ping https是否连接

在Linux系统中&#xff0c;ping通常用于测试网络上另一台主机的可达性。它使用的是ICMP协议&#xff0c;这是一种设计用来处理网络通信问题的协议。HTTPS则是一种安全的网络传输协议&#xff0c;它使用SSL/TLS加密。 如果你想要测试到某个HTTPS服务器的连接&#xff0c;你可以使…

Spring Cloud Gateway 网关

一. 什么是网关&#xff08;Gateway&#xff09; 网关就是一个网络连接到另一个网络的关口。 在同一个项目或某一层级中&#xff0c;存在相似或重复的东西&#xff0c;我们就可以将这些相似重复的内容统一提取出来&#xff0c;向前或向后抽象成单独的一层。这个抽象的过程就是…

【Linux取经路】进程通信——共享内存

文章目录 一、直接原理1.1 共享内存的的申请1.2 共享内存的释放 二、代码演示2.1 shmget2.1.1 详谈key——ftok 2.2 创建共享内存样例代码2.3 获取共享内存——进一步封装2.4 共享内存挂接——shmat2.5 共享内存去关联——shmdt2.6 释放共享内存——shmctl2.7 开始通信2.7.1 pr…

Git学习和使用指南详细篇

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

真香!Github上的这个项目,可以帮你节省打印费。

众所周知&#xff0c;我们在打印文档时&#xff0c;往往在选择黑白还是彩色打印上纠结。 全彩的文档也还好&#xff0c;最害怕的就是那种只有一部分是彩色页面&#xff0c;其他都是黑白页面的文档。 遇到这种文档&#xff0c;打印店老板好心的话会帮你分开。如果黑心一点的话…

深入了解 RabbitMQ:构建可靠消息传递系统的关键

前言 在现代分布式应用程序开发中&#xff0c;构建可靠的消息传递系统至关重要。RabbitMQ 作为一款强大的消息代理软件&#xff0c;为开发人员提供了丰富的工具和解决方案。本文将深入探讨 RabbitMQ 的核心概念、工作原理以及其在实际应用中的应用场景。 一、什么是 RabbitMQ…

java版CRM客户关系管理系统crm 客户关系管理系统-简单高效管理客户

我司的CRM客户关系管理系统是一款功能强大的客户关系管理软件&#xff0c;旨在帮助企业更有效地管理客户关系&#xff0c;提高销售效率和客户满意度。该系统涵盖了多个功能模块&#xff0c;包括待办事项、线索管理、客户管理、联系人管理、客户公海、商机管理、合同管理、回款管…

Leetcode刷题笔记3:链表基础1

导语 leetcode刷题笔记记录&#xff0c;本篇博客记录链表基础1部分的题目&#xff0c;主要题目包括&#xff1a; 203.移除链表元素707.设计链表206.反转链表 知识点 链表 链表是一种通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff0c;一个是数据…

LLM中的few-shot是什么意思

我上篇博客写了我做的测试Baichuan2-13B模型的一些工作&#xff0c;测试过程免不了要修改代码&#xff0c;在代码中接触了下所谓的few-shot。 比如&#xff0c;所谓2-shot&#xff0c;就是在提示词里提供两个问题和答案&#xff0c;让大模型以为自己回答过问题&#xff0c;后面…

vscode插件-03 PHP

PHP Intelephense 如果php在远程计算机上&#xff0c;要把插件安装在远程&#xff0c;而不是本地。 这个插件&#xff0c;要求php版本大于7&#xff0c;且设置环境变量&#xff08;好像不一定要设置&#xff09;。 设置里面搜索php.executablePath&#xff0c;打开setting.js…

Windows系统安装OpenSSH使用VScode远程连接内网Linux服务器开发

文章目录 &#x1f4a1;推荐 前言1、安装OpenSSH2、VS Code配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网…

【详细讲解】二叉树的层序遍历

广度优先搜索 总结一下&#xff0c;思路就是&#xff1a; 加入元素&#xff0c;记录size&#xff0c;size就是当前这一层的元素个数。不断弹出元素&#xff0c;size - 1&#xff0c; 同时加入弹出元素的左右孩子&#xff0c;直到size0&#xff0c;说明当前层已经完全遍历完&am…