【Linux】进程通信——管道

news2024/11/17 0:03:12

在这里插入图片描述

欢迎来到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.cc
	g++ -o $@ $^ -std=c++11
server:server.cc
	g++ -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.coloradmin.cn/o/1415073.html

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

相关文章

【DeepLearning-9】YOLOv5模型网络结构中加入MobileViT模块

一、神经网络的前中后期 在神经网络中&#xff0c;特别是在深度卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;“网络早期&#xff08;低层&#xff09;”、“网络中期&#xff08;中层&#xff09;”和“网络后期&#xff08;高层&#xff09;”通常指的是网络结构中…

sqli-labs靶场(1-6关)

1、第一关 测试id1 id1加一个引号报错&#xff0c;两个引号正常&#xff0c;应该是字符&#xff0c;还有回显 猜测字段长度 id1 order by 3 -- id1 order by 4 -- 字段长度为三&#xff0c;接下来确定位置&#xff1a;id1 and 12 union select 1,2,3 -- 查出库名,及版本号id1 …

用C语言实现贪吃蛇游戏!!!(破万字)

前言 大家好呀&#xff0c;我是Humble&#xff0c;不知不觉在CSND分享自己学过的C语言知识已经有三个多月了&#xff0c;从开始的C语言常见语法概念说到C语言的数据结构今天用C语言实现贪吃蛇已经有30余篇博客的内容&#xff0c;也希望这些内容可以帮助到各位正在阅读的小伙伴…

深度学习(6)--Keras项目详解

目录 一.项目介绍 二.项目流程详解 2.1.导入所需要的工具包 2.2.输入参数 2.3.获取图像路径并遍历读取数据 2.4.数据集的切分和标签转换 2.5.网络模型构建 2.6.绘制结果曲线并将结果保存到本地 三.完整代码 四.首次运行结果 五.学习率对结果的影响 六.Dropout操作…

N-141基于springboot,vue网上拍卖平台

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vueelementUI 服务端技术&#xff1a;springbootmybatis-plusredi…

一张图区分Spring Task的3种模式

是的&#xff0c;只有一张图&#xff1a; fixedDelay 模式cron 模式fixedRate 模式

2024/1/28周报

文章目录 摘要Abstract文献阅读题目引言方法The ARIMA modelTime delay neural network (TDNN) modelLSTM and DLSTM model 评估准则实验数据描述实验结果 深度学习AttentionAttention思想公式步骤 Attention代码实现注意力机制seq2seq解码器Model验证 总结 摘要 本周阅读了一…

腾讯云幻兽帕鲁4核16G/8核32G/16核64G服务器配置价格表

腾讯云幻兽帕鲁服务器4核16G、8核32G和16核64G配置可选&#xff0c;4核16G14M带宽66元一个月、277元3个月&#xff0c;8核32G22M配置115元1个月、345元3个月&#xff0c;16核64G35M配置580元年1个月、1740元3个月、6960元一年&#xff0c;腾讯云百科txybk.com分享腾讯云幻兽帕鲁…

uniapp组件库fullScreen 压窗屏的适用方法

目录 #平台差异说明 #基本使用 #触发压窗屏 #定义压窗屏内容 #注意事项 所谓压窗屏&#xff0c;是指遮罩能盖住原生导航栏和底部tabbar栏的弹窗&#xff0c;一般用于在APP端弹出升级应用弹框&#xff0c;或者其他需要增强型弹窗的场景。 警告 由于uni-app的Bug&#xff0…

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

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

探索IOC和DI:解密Spring框架中的依赖注入魔法

IOC与DI的详细解析 IOC详解1 bean的声明2 组件扫描 DI详解 IOC详解 1 bean的声明 IOC控制反转&#xff0c;就是将对象的控制权交给Spring的IOC容器&#xff0c;由IOC容器创建及管理对象。IOC容器创建的对象称为bean对象。 要把某个对象交给IOC容器管理&#xff0c;需要在类上…

基于springboot+vue的在线教育系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

如何看待程序员抄代码还拿着高薪这一说法?

程序员的工资构成&#xff1a;会复制粘贴值1块&#xff0c;知道去哪复制值5K&#xff0c;知道粘贴在哪值10K&#xff0c;粘贴完了能跑起来值15 有人说&#xff1a;能带领一伙人复制粘贴值20k。 有人说&#xff1a;能写一个自动复制粘贴的系统值30k。 有人纳闷问到&#xff1a…

兄弟DCP-7057黑白激光多功能一体机加粉后清零方法

硒鼓加粉机器上清零&#xff0c;方法如下&#xff1a; 打开安装硒鼓的前盖。按“清除”键&#xff0c;显示“更换硒鼓”。不用管提示&#xff0c;接着按“启用Start”&#xff0c;再按“”&#xff0c;屏幕上显示“01”。继续按“”&#xff0c;直到屏幕上显示“11”。按“OK”…

【C/C++】C/C++编程——变量和常量

文章目录 变量变量的声明变量命名规则变量的类型 常量常量的定义与初始化字面量常量整型常量浮点型常量字符常量常量表达式&#xff08;constexpr&#xff09; 大家好&#xff0c;我是 shopeeai&#xff0c;也可以叫我虾皮&#xff0c;中科大菜鸟研究生。今天我们来一起来学习C…

软考之项目管理

一、考点分布 盈亏平衡分析&#xff08;※&#xff09;进度管理&#xff08;※※※&#xff09;软件质量管理&#xff08;※※&#xff09;软件配置管理&#xff08;※※&#xff09; 二、盈亏平衡分析 正常情况下&#xff0c;销售额固定成本可变成本税费利润 盈亏平衡下&#…

微信朋友圈新功能:多账号同步发圈,定时发圈!

​你是否会有这种烦恼 想要发布一条朋友圈&#xff0c;但是却因为忙着搞其他事情无暇顾及&#xff0c;甚至忘记了需要发布朋友圈这个事情&#xff1f; 有多个微信号想要同时为它们发布同一条内容的朋友圈&#xff0c;但又不想要分别登录进去进行操作&#xff1f; 你是否厌倦了每…

算法刷题:p1387 最大正方形

解题思路&#xff1a; 利用动态规划的思想设置一个标记数组flag[][]&#xff0c;flag[i][j]用来记录矩阵op[][]中以op[i][j]为右下角的子矩阵中最大的正方形边长&#xff0c;那么动态方程就是 flag[i][j]min(flag[i-1][j],min(flag[i-1][j-1],flag[i][j-1]))1;左侧和上方以及左…

Java基础—面向对象OOP—17类与对象(创建、构造器、创建对象时简单内存分析)

把握重点&#xff0c;重点已标注&#xff0c;这篇笔记分了4个章节&#xff0c;重点看二、三、四 一、整体思维--重点把握面向对象的本质和特点 1、面向对象编程OOP&#xff1a; Object-Oriented programming 2、面向过程与面向对象 面向过程&#xff1a;线性思维 面向对象…

k8s从初识到上天系列第二篇:kubernetes的组件和架构

&#x1f609;&#x1f609; 欢迎加入我们的学习交流群呀&#xff01; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring、SpringSecurity、Docker、Grpc、各种MQ、Rpc、SpringCloud等等很多应用和源码…