【Linux】进程通信——管道

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析
🌎推荐文章:【LeetCode】winter vacation training

在这里插入图片描述


目录

  • 📋进程通信的目的
  • 📋管道
    • 匿名管道
      • pipe函数创建匿名管道
    • 管道的5种特性4种情况
    • 站在文件描述符的角度看管道
    • 命名管道
    • 命名管道和匿名管道的区别
  • 📋命名管道实现两个毫不相干进程间的读写联系
    • Makefile
    • common.h
    • server.cc(读)
    • client.cc(写)

📋进程通信的目的

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

📋管道

进程通信的管道底层原理是使用操作系统提供的文件描述符来实现。

在Linux中,管道是通过内核中的缓冲区来实现进程间数据传输的。管道可以被看作是一个字节流,它有两个文件描述符:**一个用于读取数据,一个用于写入数据。**这两个文件描述符分别被称为管道的读端和写端。当一个进程往管道的写端写入数据时,数据会被放入管道的缓冲区中,而另一个进程从管道的读端读取数据时,数据会从缓冲区中被取出。

具体来说,管道的底层原理如下:

  1. 创建管道:当调用pipe()函数时,操作系统会创建一个管道,并返回两个文件描述符,一个用于读取数据,一个用于写入数据。

  2. 数据传输:一个进程可以通过write()系统调用将数据写入管道的写端,数据会被放入管道的缓冲区中。另一个进程可以通过read()系统调用从管道的读端读取数据,数据会从缓冲区中被取出。读写操作是原子的,即每次写入或读取的数据大小是固定的。

  3. 阻塞与非阻塞:管道的写端和读端都可以设置为阻塞或非阻塞模式。在阻塞模式下,如果写端或读端没有准备好(缓冲区已满或为空),相应的写入或读取操作会被阻塞,直到准备好为止

匿名管道

当谈到 Linux 的匿名管道时,我们指的是一种特殊的进程间通信机制。它允许一个进程将输出直接发送给另一个进程,而无需使用临时文件其他形式的中间存储

匿名管道使用竖线符号(|)来表示,通过将一个进程的标准输出连接到另一个进程的标准输入来实现数据传输。这种连接使得第一个进程的输出变为第二个进程的输入,实现了进程间的数据流动。

在 Linux 中,匿名管道是通过 pipe 系统调用创建的。它返回两个文件描述符,一个用于读取数据,另一个用于写入数据。这两个文件描述符可以用于在相关进程之间传输数据。

以下是一个简单的示例来说明匿名管道的使用:

$ ls | grep "txt"

在这个示例中,ls 命令列出当前目录下的所有文件,并将输出通过匿名管道传递给 grep 命令。grep 命令会过滤出包含 “txt” 的文件。

匿名管道对于在 Linux 上进行进程间通信非常有用。然而,它也有一些限制,比如只能实现单向通信,只能用于有亲缘关系的进程(例如父子进程或兄弟进程),并且在数据量较大时可能会引起阻塞。

pipe函数创建匿名管道

pipe 函数是一个系统调用,用于在 Linux 系统中创建一个匿名管道。

它的函数原型如下:

#include <unistd.h>int pipe(int pipefd[2]);

pipe 函数接受一个整型数组 pipefd 作为参数,该数组包含两个文件描述符。pipefd[0] 是管道的读取端,pipefd[1] 是管道的写入端。

当成功调用 pipe 函数时,它会创建一个无名管道,并将对应的文件描述符存储在 pipefd 数组中。这样,我们就可以使用这两个文件描述符来实现进程间的通信。

以下是一个简单的示例,展示了如何使用 pipe 函数创建管道并进行进程间通信:

#include <unistd.h>
#include <stdio.h>
#include <string.h>int main() {int pipefd[2];char buffer[20];// 创建管道if (pipe(pipefd) == -1) {perror("pipe");return 1;}// 创建子进程pid_t pid = fork();if (pid < 0) {perror("fork");return 1;}if (pid == 0) {// 子进程从管道中读取数据close(pipefd[1]);  // 关闭写入端read(pipefd[0], buffer, sizeof(buffer));printf("子进程收到消息:%s\n", buffer);close(pipefd[0]);  // 关闭读取端} else {// 父进程向管道中写入数据close(pipefd[0]);  // 关闭读取端const char* message = "Hello, child!";write(pipefd[1], message, strlen(message) + 1);close(pipefd[1]);  // 关闭写入端}return 0;
}

在这个示例中,父进程创建了一个管道,并通过 write 函数向管道中写入消息。子进程通过 read 函数从管道中读取消息,并输出到控制台。

需要注意的是,为了正确使用管道,我们需要在适当的时候关闭文件描述符。父进程关闭了读取端,子进程关闭了写入端。

以上示例代码可以分为三个步骤
1.建立管道
2.创建子进程
3.父子关闭不需要的fd,形成单向通信的管道

管道的5种特性4种情况

🌈 管道的4种情况
1.如果管道没有数据了,读端就只能等待
2.如果管道被写满了,写端必须等待,直到有空间为止
3.写端关闭,读端一直读取,读端读到read返回值为0,表示读到文件结尾
4.读端关闭,写端一直写入,OS会直接杀掉写端进程,通过向目标进程发送SIGPIPE(13)信号,终止目标进程

🌈 管道的5钟特性
1.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创
建,然后该进程调用fork,此后父、子进程之间就可应用该管道
2.匿名管道,默认要给读写端提供同步机制
3.匿名管道是面向字节流的(假如写了n量的数据,不一定要全部读入,根据自己的设置需求,想怎么读就怎么读)
4.管道的生命周期是随进程的,进程结束,管道结束
5.管道是单向通信的,半双工通信的一种特殊情况

站在文件描述符的角度看管道

父进程中两个文件描述符分别指向读和写,子进程继承父进程的文件描述符表。
父进程断开写的连接,子进程断开读的连接,最后形成子写,父读
在这里插入图片描述

命名管道

命名管道(Named Pipe),也被称为 FIFO(First In First Out),是一种在 Linux 系统中用于进程间通信的机制。

与匿名管道不同的是,命名管道是通过文件系统中的一个特殊文件来实现的。它具有一个在文件系统中唯一标识的名称,并且可以由多个进程进行读写操作。

要创建一个命名管道,我们可以使用 mkfifo 命令或者在 C 语言中使用 mkfifo 函数。下面是一个示例,演示了如何使用命名管道进行进程间通信:

🍎命令行示例:

首先,在命令行中创建一个命名管道:

$ mkfifo mypipe

然后,在一个终端中执行以下命令,将消息写入命名管道:

$ echo "Hello, named pipe!" > mypipe

最后,在另一个终端中执行以下命令,从命名管道中读取消息:

$ cat mypipe

你将看到第二个终端输出了刚才写入的消息。

🍎C 语言示例:

以下是在 C 语言中使用命名管道进行进程间通信的示例:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>int main() {const char* fifo_file = "mypipe";const char* message = "Hello, named pipe!";char buffer[256];// 创建命名管道mkfifo(fifo_file, 0666);// 打开命名管道进行写操作int fd = open(fifo_file, O_WRONLY);write(fd, message, strlen(message) + 1);close(fd);// 打开命名管道进行读取操作fd = open(fifo_file, O_RDONLY);read(fd, buffer, sizeof(buffer));printf("Received message: %s\n", buffer);close(fd);// 删除命名管道unlink(fifo_file);return 0;
}

在这个示例中,我们首先使用 mkfifo 函数创建了一个命名管道,并指定了文件权限。然后,我们使用 open 函数打开命名管道进行写操作,并通过 write 函数向管道中写入消息。接着,我们再次使用 open 函数打开命名管道进行读取操作,并通过 read 函数读取管道中的消息。

需要注意的是,命名管道是阻塞的如果没有进程同时打开读取端和写入端,那么写入端的进程将会被阻塞,直到有其他进程打开读取端


命名管道和匿名管道的区别

命名管道(Named Pipe)和匿名管道(Anonymous Pipe)是两种不同的进程间通信机制,它们有以下几个区别:

  1. 创建方式:匿名管道通过 pipe 函数创建,而命名管道通过 mkfifo 函数或者命令行的 mkfifo 命令创建。

  2. 文件系统依赖性:匿名管道不依赖于文件系统,它只存在于内存中,没有对应的文件。命名管道则是在文件系统中创建了一个特殊文件,通过该文件进行读写操作。

  3. 进程间关系:匿名管道通常用于父子进程之间的通信,因为它们共享同一个进程空间。而命名管道可以用于任意进程之间的通信,只要它们可以访问到同一个命名管道文件。

  4. 生命周期:匿名管道在创建它的进程结束后自动销毁,无法被其他进程继续使用。而命名管道会一直存在于文件系统中,直到被显式地删除。

  5. 阻塞特性:匿名管道是阻塞的,如果没有进程同时打开读取端和写入端,写入端的进程将会被阻塞。命名管道也是阻塞的,但可以使用非阻塞方式打开以避免阻塞。

  6. 容量限制:匿名管道的容量是有限的,通常是几千字节。命名管道的容量取决于文件系统,一般比匿名管道大得多。

📋命名管道实现两个毫不相干进程间的读写联系

Makefile

.PHONY:all
all:server client #依赖关系,生成全部client:client.ccg++ -o $@ $^ -std=c++11
server:server.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f server client .fifo

common.h

#pragma once#define FILENAME ".fifo"

server.cc(读)

#include<iostream>
#include<cstring>
#include<cerrno>
#include <sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>#include "common.h"
using namespace std;int main()
{int n = mkfifo(FILENAME,0666);//创建命名管道if(n<0){cerr<<"errno:"<<errno<<",errstring:"<<strerror(errno)<<endl;return 1;}cout<<"mkfifo success...read"<<endl;int rfd = open(FILENAME,O_RDONLY);if(rfd<0){cerr<<"errno:"<<errno<<",errstring:"<<strerror(errno)<<endl;return 2;}char buffer[1024];while(true){ssize_t s = read(rfd,buffer,sizeof(buffer)-1);//读端if(s>0){buffer[s] = 0;//将最后一位置为反斜杠cout<<"Client say# "<<buffer<<endl;}else if(s==0){cout<<"client quit,server quit too"<<endl;break;}}close(rfd);cout<<"close fifo success..."<<endl;return 0;
}

client.cc(写)

#include<iostream>
#include<cstring>
#include<cerrno>
#include <sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>#include "common.h"
using namespace std;int main()
{int wfd = open(FILENAME,O_WRONLY);if(wfd<0){cerr<<"errno:"<<errno<<",errstring:"<<strerror(errno)<<endl;return 1;}cout<<"open fifo success...write"<<endl;string message;while(true){cout<<"Please Enter# ";getline(cin,message);ssize_t s = write(wfd,message.c_str(),message.size());//写端if(s<0){cerr<<"errno:"<<errno<<",errstring:"<<strerror(errno)<<endl;break;}}close(wfd);return 0;
}

在这里插入图片描述

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

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

相关文章

Linux中的软链接与硬链接

Linux链接概念 Linux链接分两种&#xff0c;一种被称为硬链接&#xff08;Hard Link&#xff09;&#xff0c;另一种被称为符号链接&#xff08;Symbolic Link&#xff09;。默认情况下&#xff0c;使用 ln 命令不加参数创建硬链接&#xff0c;加 -s 参数则创建软链接 硬链接…

[每日一题] 01.27 - 斐波那契数列

文章目录 打分斐波那契数列 打分 n int(input()) lis list(map(int,input().split())) a sum(lis) - min(lis) - max(lis) print(round(a / (n - 2),2))斐波那契数列 n int(input()) res [] for i in range(n):res.append(int(input()))Max max(res) lis [1,1] for i in…

windows上使用anconda安装tensorrt环境

windows上使用anconda安装tensorrt环境 1 安装tensorrt1.1 下载最新的稳定的tensorrt 8.6.1(tensorrt对应的cuda、cudnn等版本是参考链接4)1.2 将tensorrt添加到环境变量1.3 安装tensorrt依赖1.4 安装Pycuda1.5 安装pytorch 2 测试2.1 测试TensorRT 样例(这个测试主要来源于参考…

C++设计模式介绍:优雅编程的艺术

物以类聚 人以群分 文章目录 简介为什么有设计模式&#xff1f; 设计模式七大原则单一职责原则&#xff08;Single Responsibility Principle - SRP&#xff09;开放封闭原则&#xff08;Open/Closed Principle - OCP&#xff09;里氏替换原则&#xff08;Liskov Substitution …

hardware simulation——编译框架搭建

目录 前言 学习Linux的makefile 规范化配置 文件生成过程描述 编码和验证 前言 编译框架搭建遇到了些问题&#xff0c;我对makefile不是那么熟练&#xff0c;能力只停留在能看懂和能改上自己独立写个大工程的编译框架有困难&#xff0c;所以这期我们一起看linux内核的编译…

Mac Monitor:一款为macOS安全研究量身定制的高级独立系统监控工具

关于Mac Monitor Mac Monitor是一款功能强大的高级独立系统安全监控工具&#xff0c;该工具专为macOS安全研究、恶意软件分类和系统故障排除而设计&#xff0c;主要基于Apple Endpoint Security&#xff08;ES&#xff09;实现其功能。 Mac Monitor能够收集各种类型的系统事件…

Nginx与keepalived实现集群

提醒一下&#xff1a;下面实例讲解是在mac虚拟机里的Ubuntu系统演示的&#xff1b; Nginx与keepalived实现集群实现的效果 两台服务器都安装Nginx与keepalived&#xff1a; master服务器的ip(192.168.200.2) backup服务器的ip(192.168.200.4) 将 master服务器Nginx与keepalive…

LeetCode.11. 盛最多水的容器

题目 题目链接 分析 这道题的意思就是让我们找两个下标&#xff0c;以这两个下标组成的线为底&#xff0c;高度取这两个位置对应数字的最小值为高&#xff0c;组成一个长方形&#xff0c;求长方形最大的面积可以为多少。 暴力的解法是什么&#xff1f;&#xff1f;&#xf…

环形链表的检测与返回

环形链表 王赫辰/c语言 - Gitee.com 快慢指针的差距可以为除一以外的数吗&#xff1f;不可以如果差奇数则无法发现偶数环&#xff0c;是偶数无法发现奇数环&#xff0c;本题思路为指针相遇则为环&#xff0c;而以上两种情况会稳定差一&#xff0c;导致指针永不相遇 最终返回…

【C++中STL】list链表

List链表 基本概念构造函数赋值和交换大小操作插入和删除数据存取反转和排序 基本概念 将数据进行链式存储 链表list是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的&#xff0c;链表是由一系列结点组成&#xff0c;结点的组…

深度强化学习(王树森)笔记04

深度强化学习&#xff08;DRL&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接&#xff1a;https://github.com/wangshusen/DRL 源代码链接&#xff1a;https://github.c…

字符串和C预处理器

本文参考C Primer Plus第四章学习 文章目录 常量和预处理器const限定符 1. 常量和预处理器 有时&#xff0c;在程序中要使用常量。例如&#xff0c;可以这样计算圆的周长&#xff1a; circumference 3.14159 * diameter; 这里&#xff0c;常量3.14159 代表著名的常量 pi(π)。…