【Linux】进程间通信 - 管道

文章目录

  • 1. 进程间通信介绍
    • 1.1 进程间通信目的
    • 1.2 进程间通信发展
    • 1.3 进程间通信分类
  • 2. 管道
    • 2.1 什么是管道
    • 2.2 匿名管道
    • 2.3 用 fork 来共享管道原理
    • 2.4 站在文件描述符角度 - 深入理解管道
    • 2.5 站在内核角度 - 管道本质
    • 2.6 管道读写规则
    • 2.7 管道特点
  • 3. 命名管道
    • 3.1 匿名管道与命名管道的区别
    • 3.2 命名管道的打开规则
    • 例:用命名管道实现 server & client 通信

在这里插入图片描述

1. 进程间通信介绍

两个进程间,可以进行“数据”的直接传递吗?不能!进程具有独立性!

进程间通信的本质:先让不同的进程,看到同一份资源(一般都是要由 OS 提供)。

1.1 进程间通信目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程。
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如 Debug 进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

1.2 进程间通信发展

  • 管道
  • System V 进程间通信
  • POSIX 进程间通信

1.3 进程间通信分类

  • 管道
    • 匿名管道 pipe
    • 命名管道
  • System V IPC
    • System V 消息队列
    • System V 共享内存
    • System V 信号量
  • POSIX IPC
    • 消息队列
    • 共享内存
    • 信号量
    • 互斥量
    • 条件变量
    • 读写锁

2. 管道

2.1 什么是管道

  • 管道是 Unix 中最古老的进程间通信的形式。
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。

在这里插入图片描述

2.2 匿名管道

#include <unistd.h>功能:创建一个无名管道
原型:
int pipe(int fd[2]);
参数:
fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
返回值:成功返回0,失败返回错误代码

在这里插入图片描述

实例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>// 例子:从键盘读取数据,写入管道,读取管道,写到屏幕
int main()
{int fds[2];char buf[100];int len;if (pipe(fds) == -1)perror("make pipe"), exit(1);// read from stdinwhile (fgets(buf, 100, stdin)){len = strlen(buf);// write into pipeif (write(fds[1], buf, len) != len){perror("write to pipe");break;}memset(buf, 0x00, sizeof(buf));// read from pipeif ((len = read(fds[0], buf, 100)) == -1){perror("read from pipe");break;}// write to stdoutif (write(1, buf, len) != len){perror("write to stdout");break;}}return 0;
}

2.3 用 fork 来共享管道原理

在这里插入图片描述

2.4 站在文件描述符角度 - 深入理解管道

在这里插入图片描述

2.5 站在内核角度 - 管道本质

在这里插入图片描述

所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了 “Linux 一切皆文件” 思想。

fork 共享管道测试代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>// child
void writer(int wfd)
{const char *str = "hello father, I am child";char buffer[128];int cnt = 0;pid_t pid = getpid();while (1){sleep(1);char c = 'A';write(wfd, &c, 1);cnt++;printf("cnt: %d\n", cnt);}close(wfd);
}// father
void reader(int rfd)
{char buffer[1024];int cnt = 10;while (1){ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);if (n > 0)printf("father get a message: %s, n: %ld\n", buffer, n);else if (n == 0){printf("read pipe done, read file done!\n");break;}elsebreak;cnt--;if (cnt == 0)break;}close(rfd);printf("read endpoint close!\n");
}int main()
{printf("PIPE_BUF: %d\n", _PC_PIPE_BUF);// 1.int pipefd[2];int n = pipe(pipefd);if (n < 0)return 1;//                                       read       writeprintf("pipefd[0]: %d, pipefd[1]: %d\n", pipefd[0], pipefd[1]); // 3, 4// 2.pid_t id = fork();if (id == 0){// child: wclose(pipefd[0]);writer(pipefd[1]);exit(0);}// father: rclose(pipefd[1]);reader(pipefd[0]);int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid == id){printf("exit code: %d, exit signal: %d\n", WEXITSTATUS(status), status & 0x7F);}return 0;
}

2.6 管道读写规则

  1. 当没有数据可读时
    • O_NONBLOCK disable:read 调用阻塞,即进程暂停执行,一直等到有数据来到为止。
    • O_NONBLOCK enable:read 调用返回 -1,errno 值为 EAGAIN。
  2. 当管道满的时候
    • O_NONBLOCK disable:write 调用阻塞,直到有进程读走数据。
    • O_NONBLOCK enable:调用返回 -1,errno 值为 EAGAIN。
  3. 如果所有管道写端对应的文件描述符被关闭,则 read 返回 0。
  4. 如果所有管道读端对应的文件描述符被关闭,则 write 操作会产生信号 SIGPIPE,进而可能导致 write 进程退出。
  5. 当要写入的数据量不大于 PIPE_BUF 时,Linux 将保证写入的原子性。
  6. 当要写入的数据量大于 PIPE_BUF 时,Linux 将不再保证写入的原子性。

2.7 管道特点

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用 fork,此后父、子进程之间就可应用该管道。
  • 管道提供流式服务。
  • 一般而言,进程退出,管道释放,所以管道的生命周期随进程。
  • 一般而言,内核会对管道操作进行同步与互斥。
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。

在这里插入图片描述

3. 命名管道

  • 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:

    $ mkfifo filename
    
  • 命名管道也可以从程序里创建,相关函数有:

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

创建命名管道:

int main(int argc, char* argv[])
{mkfifo("p2", 0644);return 0;
}

3.1 匿名管道与命名管道的区别

  • 匿名管道由 pipe 函数创建并打开。
  • 命名管道由 mkfifo 函数创建,打开用 open。
  • FIFO(命名管道)与 pipe(匿名管道)之间唯一的区别在于它们创建与打开的方式不同,一旦这些工作完成之后,它们具有相同的意义。

3.2 命名管道的打开规则

  • 如果当前打开操作是为读而打开 FIFO 时:
    • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该 FIFO。
    • O_NONBLOCK enable:立刻返回成功。
  • 如果当前打开操作是为写而打开 FIFO 时:
    • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该 FIFO。
    • O_NONBLOCK enable:立刻返回失败,错误码为 ENXIO。

例:用命名管道实现 server & client 通信

$ ls -l
total 16
-rw-rw-r-- 1 ubuntu ubuntu 1009 May  4 17:16 Comm.hpp
-rw-rw-r-- 1 ubuntu ubuntu  193 May  4 15:14 Makefile
-rw-rw-r-- 1 ubuntu ubuntu  656 May  4 17:44 PipeClient.cc
-rw-rw-r-- 1 ubuntu ubuntu  929 May  4 17:44 PipeServer.cc

Makefile:

$ cat Makefile 
.PHONY:all
all:pipe_client pipe_serverpipe_server:PipeServer.ccg++ -o $@ $^ -std=c++11
pipe_client:PipeClient.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f pipe_client pipe_server

Comm.hpp:

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>using namespace std;#define Mode 0666
#define Path "./fifo"class Fifo
{
public:Fifo(const string &path): _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);if (n == 0){cout << "mkfifo success" << endl;}else{cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}~Fifo(){int n = unlink(_path.c_str());if (n == 0){cout << "remove fifo file " << _path << " success" << endl;}else{cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}private:string _path; // 文件路径 + 文件名
};#endif

PipeServer.cc:

#include "Comm.hpp"int main()
{Fifo fifo(Path);int rfd = open(Path, O_RDONLY);if (rfd < 0){cerr << "open failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;return 1;}// 如果我们的写端没打开,先读打开,open的时候就会阻塞,直到把写端打开,读open才会返回cout << "open success" << endl;char buffer[1024];while (true){ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;cout << "client sat : " << buffer << endl;}else if (n == 0){cout << "client quit, me too!" << endl;break;}else{cerr << "read failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;break;}}close(rfd);return 0;
}

PipeClient.cc:

#include "Comm.hpp"int main()
{int wfd = open(Path, O_WRONLY);if (wfd < 0){cerr << "open failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;return 1;}string inbuffer;while (true){cout << "Please Enter Your Message# ";std::getline(cin, inbuffer);if (inbuffer == "quit")break;ssize_t n = write(wfd, inbuffer.c_str(), inbuffer.size());if (n < 0){cerr << "write failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;break;}}close(wfd);return 0;
}

结果:

在这里插入图片描述


END

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

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

相关文章

linux代码实操——信号的使用

信号的基本概念 信号是系统响应某个条件而产生的事件&#xff0c;进程接收到信号会执行相应的操作。 与信号有关的系统调用在“signal.h”头文件中有声明 常见信号的值&#xff0c;及对应的功能说明&#xff1a; 修改信号的响应方式 – signal() 我们来做个小实验: 在键盘上…

推荐书单|提升境界、思维能力

1、《别做正常的傻瓜》 豆瓣评分&#xff1a;8.1 通过揭示人们在日常生活中常见的非理性行为&#xff0c;引导读者认识并克服这些行为&#xff0c;从而做出更明智的决策。 2、《活法》 豆瓣评分&#xff1a;8.1 稻盛和夫分享其人生哲学和经营哲学的著作&#xff0c;强调了正确…

软设之段页式存储

页式存储&#xff1a;将程序与内存划分为同样大小的块&#xff0c;以页为单位将程序调入内存。 比如说每页是1M&#xff0c;不管是内存还是程序&#xff0c;每页都是1M 高级程序语言使用逻辑地址&#xff0c;比如C语言用的指针。 运行状态时&#xff0c;内存中使用物理地址。…

【C++】模板初阶:泛型编程的起点

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

富文本编辑器CKEditor4简单使用-08(段落首行缩进插件 + 处理粘贴 Microsoft Word 中的内容后保持原始内容格式(包括首行缩进))

富文本编辑器CKEditor4简单使用-08&#xff08;段落首行缩进插件 处理粘贴 Microsoft Word 中的内容后保持原始内容格式&#xff08;包括首行缩进&#xff09;&#xff09; 1. 缩进&#xff0c;特殊方式处理——修改原工具栏里的增加缩进量2 缩进&#xff0c;插件处理2.1 下载…

并查集~~

值为v的数封为Node型的节点&#xff0c;除了nodes中保存了V和Node的对应关系外&#xff0c;并查集相关的parents&#xff0c;sizeMap都是用Node来关联的。 #include<iostream> #include<map> #include<vector> #include<stack> using namespace std;t…

建一个wordpress外贸独立站的费用构成与分析

外贸独立站因为独特的优势&#xff0c;越来越被做外贸的跨境电商所青睐。简站主题经常接到做外贸的老板咨询&#xff0c;建一个wordpress外贸独立站大约多少钱&#xff0c;也是被问的最多的问题之一。下面小简就从几个方面讲一下&#xff0c;决定一个wordpress外贸独立站的费用…

TypeScript的使用:可以谈谈TypeScript的优点以及如何在实际项目中引入和使用TypeScript,包括一些基本的语法和编程技巧。

TypeScript是JavaScript的一个超集&#xff0c;它主要提供了类型系统和对ES6的兼容。下面是一些TypeScript的主要优点&#xff1a; 静态类型检查&#xff1a;这是TypeScript最主要的功能&#xff0c;它可以在编译阶段发现和报告错误&#xff0c;帮助开发者提前找到问题。更好的…

TensorFlow轻松入门(二)——小案例:ANN构建一个或运算的模型

或运算&#xff1a; 位与位进行比较&#xff0c;如果有任一个是1&#xff0c;结果为1&#xff1b;两个都为0&#xff0c;结果则为0。 实现步骤 构建Feature与Label数据 创建顺序模型 指定模型的第一层&#xff0c;线性模型 添加一层激活函数 模型编译 模型训练 模型预测…

GitHub 异常——无法连接22端口:Connection timed out

GitHub 异常——无法连接22端口&#xff1a;Connection timed out 问题描述原因分析&#xff1a;解决方案&#xff1a;参考 问题描述 正常配置并使用使用SSH方式&#xff0c;使用以下命令git clone、git pull、git push&#xff0c;报错如下&#xff1a; ssh: connect to host …

C语言-链表实现贪吃蛇控制台游戏

使用C语言和链表实现贪吃蛇游戏 一、引言 贪吃蛇游戏是一个经典的游戏&#xff0c;它的玩法简单而富有挑战性。在这个博客中&#xff0c;我将分享如何使用C语言和链表数据结构来自主实现贪吃蛇游戏。我会详细介绍游戏的设计思路、编码过程、遇到的问题及解决方案&#xff0c;…

翻译《The Old New Thing》 - Why does the CreateProcess function do autocorrection?

Why does the CreateProcess function do autocorrection? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20050623-03/?p35213 Raymond Chen 在 2005 年 6 月 23 日 为什么 CreateProcess 函数会进行自动更正&#xff1f; 译注&#xff…

【华为OD机试C卷D卷】符号运算(C++/Java/Python)

【华为OD机试】-(A卷+B卷+C卷+D卷)-2024真题合集目录 【华为OD机试】-(C卷+D卷)-2024最新真题目录 题目描述 给定一个表达式,求其分数计算结果。 表达式的限制如下: 所有的输入数字皆为正整数(包括0) 仅支持四则运算(+-*,/)和括号 结果为整数或分数,分数必须化为最简格…

13_Scala面向对象编程_伴生对象

文章目录 1.伴生对象1.1 scala的一个性质&#xff0c;scala文件中的类都是公共的&#xff1b;1.2 scala使用object关键字也可以声明对象&#xff1b; 3.关于伴生对象和类4.权限修饰符&#xff0c;scala仅有private;5.伴生对象可以访问伴生类中的私有属性&#xff1b;6.案例7.伴…

世界十大最具影响力人物颜廷利:真正的高人,靠谱的行为

对于真正的‘高人’&#xff0c; 在面对‘狗洞’时&#xff0c; 他们都比较理智&#xff0c; 从来都不趾高气扬&#xff0c; 因为他们晓得&#xff0c; 倘若说不能弯下腰&#xff0c; 并而直立着身子走路的话&#xff0c; 那么&#xff0c; 他们就不是纯粹的‘高人’&#xff0…

华为OD机试题:字符串变换最小字符串

题目描述&#xff1a; 给定一个字符串s&#xff0c;最多只能进行一次变换&#xff0c;返回变换后能得到的最小字符串&#xff08;按照字典序进行比较&#xff09;。 变换规则&#xff1a;交换字符串中任意两个不同位置的字符。 一串小写字母组成的字符串s 输出描述 按照要求进…

启程Python机器学习之旅:从JupyterLab到神经网络初探

引言 在数据科学和人工智能的浪潮中&#xff0c;Python已经成为最受欢迎的编程语言之一。其简洁的语法和强大的库支持&#xff0c;使得从科研到商业的各个领域都能见到Python的身影。今天&#xff0c;我们将通过JupyterLab——一个交互式的开发环境&#xff0c;开始我们的Pyth…

1850H-The Third Letter

题目链接&#xff1a;The Third Letter 本道题目就是带权并查集的模板题&#xff0c;但又好久没学忘了&#xff0c;再复习一遍。。。 路径压缩函数模板&#xff1a; int root(int x){if(pre[x]!x){int troot(pre[x]);d[x]d[pre[x]];pre[x]t;}return pre[x]; } 之后就模拟一…

c++ 红黑树学习及简单实现

1. 了解红黑树 1.1. 概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个节点增加一个存储位表示节点的颜色&#xff0c;可以是红色&#xff0c;或是黑色&#xff0c;通过对任何一条从根到叶子的路径上各个节点的着色方式进行限制&#xff0c;红黑树确保没有一条路…

初识webpack项目

新建一个空的工程 -> % mkdir webpack-project 为了方便追踪执行每一个命令&#xff0c;最终产生了哪些变更&#xff0c;将这个空工程初始化成git项目 -> % cd webpack-project/-> % git init Initialized empty Git repository in /Users/lixiang/frontworkspace/…