【Linux】进程间通信(2):命名管道

news2024/9/20 15:06:50

目录

一、命名管道的概念

二、命名管道与匿名管道的区别

1、常规区别

2、读写阻塞的区别

3、存储位置和数据处理的区别

匿名管道

命名管道

 三、命名管道的创建与使用

1、在命令行中命名管道的创建与使用

2、在编程中命名管道的创建与使用

2、示例代码

 四、命名管道的应用—模拟简易的应用端与客户端通信


一、命名管道的概念

命名管道(FIFO,First In First Out)是一种特殊的文件类型,用于在不同进程之间进行数据交换。它的设计允许一个进程将数据写入管道,而另一个进程从管道中读取数据。这种机制使得不同进程能够通过管道进行通信,而不需要直接共享内存或使用其他复杂的同步机制。

二、命名管道与匿名管道的区别

1、常规区别

  • 命名管道:

    • 命名管道是一个在文件系统中存在的特殊文件,具有一个路径名,允许不同进程通过该路径名进行数据通信。
    • 可以通过 mkfifo() 系统调用创建,并且可以在文件系统中查看和操作。
    • 命名管道在文件系统中具有可见性,可以通过路径名进行访问,任何进程只要知道管道的名称都可以进行读写操作。
    • 适用于不同进程之间的通信,尤其是没有亲缘关系的进程(例如在不同的终端或不同的应用程序之间)。由于命名管道在文件系统中存在,适合需要跨多个会话或长期运行的进程通信的场景。
  • 匿名管道:

    • 匿名管道没有名称,通常用于具有亲缘关系的进程(例如父进程和子进程)之间的通信。
    • 其创建通常通过 pipe() 系统调用实现,返回一对文件描述符(读写端)。
    • 匿名管道在创建后不具有名称,无法在文件系统中访问。只能通过创建它的进程及其子进程进行通信,其他进程无法直接访问。
    • 主要用于具有亲缘关系的进程之间的通信,如父进程和子进程。适合于短期的、临时的通信需求。常用于简单的数据传输,尤其是在创建子进程后立即进行数据传输时。

        

2、读写阻塞的区别

命名管道在管道读写阻塞的规则上与匿名管道相似,读取和写入操作均会在相应条件下发生阻塞。 如果一个进程尝试从空的命名管道中读取数据,它会被阻塞,直到有数据写入管道。同样,如果管道缓冲区已满,写入操作也会阻塞,直到有足够的空间。

但在创建管道文件完成后仍有区别。

  • 匿名管道:

    • 当使用pipe系统调用创建匿名管道成功时,管道的读写两端自动打开。
    • 无需使用open函数对匿名管道文件进行读写打开的操作。直接调用write和read函数对匿名管道进行读写即可。
  • 命名管道:

    • 当成功创建FIFO文件后,管道的读写两端需要进程主动使用open()系统调用函数打开。
    • 如果进程以只读的方式打开FIFO文件,open会阻塞,等待有进程以写的方式打开此FIFO文件才会成功返回(阻塞过程可以被信号打断)。如果使用open函数时,O_NONBLOCK标志被置位,则当没有写进程时,不进行阻塞等待,直接返回成功。
    • 类似的,如果进程以只写的方式打开FIFO文件,open会阻塞,等待有进程以读的方式打开此FIFO文件才会成功返回(阻塞过程可以被信号打断)。如果使用open函数时,O_NONBLOCK标志被置位,则当没有读进程时,不进行阻塞等待,直接返回错误,错误码为ENXIO。
    • 如果以读+写的方式打开FIFO文件,则不会发生阻塞。但单个管道用于单向通信,最好不要在同一个进程中同时以读+写的方式打开!!!

3、存储位置和数据处理的区别

匿名管道

  • 存储位置:

    • 仅存在于内存中:匿名管道不涉及文件系统,它们完全在内存中实现。它们没有文件路径,因此不在文件系统中存在,而是作为内存中的数据缓冲区来进行通信。
    • 独立于文件系统:匿名管道是进程间通信的一种机制,仅在创建它的进程及其子进程中存在。它们的生命周期通常由创建它们的进程控制,当进程结束时,匿名管道也随之消失。
  • 数据处理:

    • 数据在内存中的缓冲区中处理,读写操作都是通过内存进行的。写入的数据会在内存中缓冲,直到被读取。在此过程中,数据不会被写入到磁盘。

命名管道

  • 存储位置:

    • 文件系统中的特殊文件:命名管道通过 mkfifo() 系统调用创建,这会在文件系统中创建一个特殊的文件,这个文件代表命名管道。该文件存在于磁盘上的文件系统中,可以通过路径名进行访问。
    • 元数据存储:命名管道的元数据(如管道的名字、权限等)存储在文件系统中,这使得它们在系统重启后仍然存在(即文件系统中的信息不会丢失)。
  • 数据处理:

    • 内存中的数据流:虽然命名管道的路径名和元数据存储在磁盘上,但实际的数据流经管道的过程中会在内存中进行缓冲。写入的数据首先会存储在内存中的缓冲区中,直到被读取。在此过程中,数据不会被写入到磁盘——即文件的内容大小永远为0!

 三、命名管道的创建与使用

1、在命令行中命名管道的创建与使用

1. 创建命名管道

使用 mkfifo 命令来创建一个命名管道。mkfifo 命令会在文件系统中创建一个特殊的文件,代表命名管道。例如:

mkfifo /pathname/fifo

其中 /pathname/是命名管道的路径,fifo是命名管道的名称。例如:

这会在 当前目录下创建一个名为 fifo 的命名管道。

可以看出fifo是一种特殊的管道文件。

2. 使用命名管道

命名管道的使用可以通过标准输入/输出操作进行。

以下是一个简单的示例,展示如何使用命名管道在两个进程之间进行通信。

步骤

  1. 在一个终端中,启动一个读取进程

    cat /pathname/myfifo
    

    这个进程会等待从管道中读取数据。

  2. 在另一个终端中,启动一个写入进程

    echo "Hello from the other side!" > /pathname/myfifo
    

    这条命令将数据写入到管道中,读取进程会立即接收到数据并输出。

3. 清理

如果不再需要命名管道,可以使用 rm 命令删除它:

rm /tmp/myfifo

这会从文件系统中删除 /tmp/myfifo 命名管道文件。

2、在编程中命名管道的创建与使用

1、认识mkfifo系统调用函数

函数原型

在 C 语言中,mkfifo() 的函数原型定义在 <sys/stat.h> 头文件中,如下所示:

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

参数

  • pathname: 指定要创建的命名管道的路径和文件名。路径可以是绝对路径或相对路径。
  • mode: 指定命名管道的权限。这个参数是一个八进制数,用于设置文件的权限。例如,0666 代表所有用户都有读写权限。

返回值

  • 成功时返回 0
  • 失败时返回 -1,并设置 errno 来指示错误类型。

常见错误

  • EEXIST: 指定的路径已经存在,且不是一个 FIFO 文件。
  • ENOSPC: 文件系统没有足够的空间来创建文件。
  • EACCES: 没有权限创建文件。

2、示例代码

以下是一个简单的示例程序,演示如何使用 mkfifo() 创建命名管道,并用 open(), write(), 和 read() 操作它,需要注意的是:以下是三个不同的程序,运行时即为三个不同的进程,必须要先创建好命名管道才能对其进行读写!!!

(1)、创建命名管道

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

#define FIFO_NAME "/tmp/myfifo"

int main() {
    // 创建命名管道
    if (mkfifo(FIFO_NAME, 0666) == -1) {
        perror("mkfifo");
        exit(EXIT_FAILURE);
    }

    // 成功创建后,程序会在此停留
    printf("FIFO %s created successfully.\n", FIFO_NAME);
    return 0;
}

(2)、写入数据到命名管道

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

#define FIFO_NAME "/tmp/myfifo"

int main() {
    int fd;
    const char *message = "Hello from the writer!";

    // 打开命名管道
    fd = open(FIFO_NAME, O_WRONLY);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 写入数据
    if (write(fd, message, sizeof(message)) == -1) {
        perror("write");
        close(fd);
        exit(EXIT_FAILURE);
    }

    printf("Message written to FIFO: %s\n", message);

    // 关闭文件描述符
    close(fd);
    return 0;
}

(3)、读取数据从命名管道

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

#define FIFO_NAME "/tmp/myfifo"

int main() {
    int fd;
    char buffer[100];

    // 打开命名管道
    fd = open(FIFO_NAME, O_RDONLY);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 读取数据
    if (read(fd, buffer, sizeof(buffer)) == -1) {
        perror("read");
        close(fd);
        exit(EXIT_FAILURE);
    }

    printf("Message read from FIFO: %s\n", buffer);

    // 关闭文件描述符
    close(fd);
    return 0;
}

 四、命名管道的应用—模拟简易的应用端与客户端通信

场景模拟:客户端向服务端发送信息,服务端接收信息后对信息进行处理。

场景实现:

1、客户端与服务端分别为两个不同的进程,当服务端开始运行时需要创建一个通信管道作为信息传输的媒介。

2、客户端通过向管道中写入信息,服务端从管道中读取信息,服务端拿到信息后再对信息进行处理,进而实现不同进程间的通信。

需要注意的是,当程序退出时,我们应该主动删除命名管道文件,防止资源浪费,在这里介绍一个函数unlink():

unlink 函数用于删除文件系统中的文件或链接。在 Linux 系统中,unlink 是一个系统调用,它从文件系统中删除指定路径的文件名。如果这个文件名是该文件的最后一个名字,文件数据会被实际删除;如果还有其他文件名引用这个文件的数据,文件数据会保留,直到所有引用它的文件名都被删除。

函数原型

#include <unistd.h>

int unlink(const char *pathname);

参数

  • pathname: 要删除的文件的路径。

返回值

  • 成功时返回 0

  • 失败时返回 -1 并设置 errno 以指示错误类型。

常见错误

  • ENOENT: 指定的文件不存在。

  • EACCES: 权限被拒绝,无法删除文件。

  • EPERM: 操作不允许,如尝试删除一个目录而不是普通文件(虽然 unlink 只对文件有效,删除目录应该使用 rmdir)。

用法示例

如何使用 unlink删除一个文件:

#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>

void deleteFile(const char* filepath) {
    if (unlink(filepath) == -1) {
        std::cerr << "Error deleting file: " << std::strerror(errno) << std::endl;
    } else {
        std::cout << "File deleted successfully." << std::endl;
    }
}

int main() {
    const char* filePath = "./myfifo";
    deleteFile(filePath);
    return 0;
}

备注

  • unlink只能删除文件,而不能删除目录。如果你需要删除目录,请使用rmdir。

  • unlink对于 FIFO(命名管道)和常规文件的行为相同。当 FIFO 文件没有任何进程打开时,它会被删除。

使用场景

  • 在程序结束时或在不再需要时删除临时文件。
  • 删除命名管道(FIFO),如在 IPC(进程间通信)中创建的 FIFO 文件。

确保在适当的时候调用 unlink,例如在程序退出时,如果程序中使用了 mkfifo创建 FIFO 文件时,应主动调用unlink函数删除fifo文件,以避免留下不必要的文件。

头文件:namedpipe.hpp

#pragma once
#include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>
#include <cstring>

#define PATHNAME "./myfifo"

#define READER 0
#define WRITER 1
#define LIMIT 0666
#define BUFFER_SIZE 256

class NamePipe
{
private:
    std::string _path;
    int _fd;
    int _identity;
public:
    NamePipe(const std::string& path, int identity)
    :_path(path), _identity(identity), _fd(-1)
    {
        if(_identity == READER){
            if(mkfifo(PATHNAME, LIMIT) == -1){
                perror("create fifo fail!");
                exit(-1);
            }
        }
    }
    ~NamePipe(){
        if (_identity == READER)
        {
            int res = unlink(_path.c_str());
            if (res != 0)
            {
                perror("unlink fail!");
            }
        }
        Close();
    }
    void OpenForRead()
    {
        if((_fd = open(_path.c_str(), O_RDONLY, LIMIT)) == -1){
                perror("open for read fail!");
                exit(-1);
            }
            else{
                std::cout << "客户端准备进行通信..." << std::endl;
            }
        
    }
    int Read()
    {
        int r_num = 0;
        char buffer[BUFFER_SIZE];
        memset(buffer, 0, sizeof(buffer));
        if((r_num = read(_fd, buffer, sizeof(buffer) - 1)) == -1){
            perror("read fail!");
            return -1;
        }
        else if(r_num == 0)
        {
            std::cout << "客户端关闭,通信终止..." << buffer << std::endl;
            return 0;
        }
        else {
            buffer[r_num] = '\0';
            std::cout << "读取信息为:" << buffer << std::endl;
        }
        return r_num;
    }
    void OpenForWrite()
    {
        if((_fd = open(_path.c_str(), O_WRONLY, LIMIT)) == -1){
                perror("open for read fail!");
                exit(-1);
            }
        else{
            std::cout << "服务端端准备进行通信..." << std::endl;
        }
    }
    void Write(const std::string& buffer)
    {
        int w_num = 0;
        if((w_num = write(_fd, buffer.c_str(), buffer.size())) == -1){
            perror("read fail!");
            exit(-1);
        }
        else{
            std::cout << "写入信息成功!" << std::endl;
        }
    }
    void Close()
    {
        close(_fd);
    }
};

makefile:

.PHONY:all
all:server client

server:server.cc
	g++ server.cc -o server

client:client.cc
	g++ client.cc -o client

.PHONY:clean
clean:
	rm -f client server

server.cc

#include "namedpipe.hpp"

int main()
{
    NamePipe fifo(PATHNAME, READER);
    fifo.OpenForRead();
    while (true)
    {
        int ret = fifo.Read();
        if (ret == 0)
        {
            std::cout << "Client quit, Server Too!" << std::endl;
            break;
        }
        else if (ret == -1)
        {
            std::cout << "ReadNamedPipe Fail!" << std::endl;
            break;
        }
    }
    return 0;
}

client.cc:

#include "namedpipe.hpp"

int main()
{
    NamePipe fifo(PATHNAME, WRITER);
    fifo.OpenForWrite();
    std::string buffer;
    while(true)
    {
        std::cout << "请输入信息:";
        std::getline(std::cin, buffer);
        fifo.Write(buffer);
    }
    return 0;
}

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

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

相关文章

实验2-3-6 计算分段函数[3]

#include<stdio.h> #include<math.h> int main(){double x;scanf("%lf",&x);if (x10)printf("f(%.1f)%.1f",x,1/x);elseprintf("f(%.1f)%.1f",x,x);}

【CSS】css 如何实现固定宽高比

今天和同事讨论这个问题&#xff0c;一时间还想不到了&#xff0c;于是学习了下&#xff0c;就顺便当个记录吧 要在CSS中实现固定宽高比&#xff0c;有两种主要的方法可以选择。一种是使用新的aspect-ratio属性&#xff0c;另一种是利用padding技巧。随着现代浏览器对aspect-ra…

27、美国国家冰雪中心(NSIDC)海冰密集度月数据下载与处理

文章目录 一、前言二、数据下载三、使用Ponply查看数据结构四、代码一、前言 处理美国国家冰雪中心(NSIDC)的海冰密集度月度数据时,坐标转换是一个重要的步骤。NSIDC提供的数据通常采用极地球面坐标系,需要将其转换为常用的地理坐标系(如经纬度)以便进行分析和可视化。 坐…

【数据结构与算法】共享双向链表

共享双向链表 一.共享双向链表的引入二.共享双向链表的原理三.共享双向链表的结构四.共享双向链表的初始化五.共享双向链表插入六.共享链表遍历数据七.共享链表的销毁八.总结 一.共享双向链表的引入 当我们使用双向链表其数据域是固定的类型,如果我需要另外一种类型的话,就需要…

共聚ISC|赛宁网安即将亮相ISC.AI2024第十二届互联网安全大会

随着数字时代的飞速发展&#xff0c;网络安全和人工智能技术的融合已成为推动各行业数智化转型的关键。7月31日至8月1日&#xff0c;备受瞩目的ISC.AI 2024第十二届互联网安全大会将在北京国家会议中心隆重开幕。本届大会将全面聚焦于安全和人工智能两大核心领域&#xff0c;致…

深入探讨Facebook投放菲律宾slots游戏广告的优势

在深入探讨Facebook投放菲律宾游戏广告的优势时&#xff0c;我们不得不提及该平台在本地市场的深厚渗透力与高度互动性。菲律宾作为东南亚地区互联网用户增长最快的国家之一&#xff0c;Facebook不仅是人们日常社交的必备工具&#xff0c;更是信息获取与娱乐消费的重要渠道。因…

Animation Clips

动画剪辑是Unity用来表示对象运动姿态的基础资源&#xff0c;你可以从模型文件中导入动画剪辑&#xff0c;也可以在Unity内部创建简单的动画剪辑。 Import Animation From Model 在导入时只需要确保模型的Animation选项卡中的Import Animation是被激活的&#xff0c;对于没有动…

【Linux】Ubuntu解决Release 文件已经过期问题

​今天在执行update更新软件包时遇到了此问题&#xff1a;E: http://cn.archive.ubuntu.com/ubuntu/dists/jammy-updates/InRelease 的 Release 文件已经过期(已经过期了 247天 21小时 33分 15秒)。该仓库的更新将不会应用&#xff0c;如图 ​ 这个报错之前其实经常遇到&#x…

品酒师眼中的红酒定制:一场不同的味觉盛宴

在红酒的世界里&#xff0c;每一滴酒液都承载着丰富的情感和不同的韵味。当谈到红酒定制时&#xff0c;洒派红酒&#xff08;Bold & Generous&#xff09;无疑为品酒师们带来了一场味觉盛宴。今天&#xff0c;就让我们一起跟随品酒师的目光&#xff0c;探索红酒定制背后的魅…

【Unity渲染】GI

光照探针 光照探针就是在烘焙的时候会布局多个探测点&#xff0c;记录那个地方的光照信息&#xff0c;后面实时渲染动态物体会影响其渲染&#xff0c;动态物体会选择自己周围最近的探针进行影响 反射探针 反射探针就是记录周围环境&#xff0c;计算一个类似天空盒的图片&am…

AIGC-VDM -Video Diffusion Models论文解读

homepage&#xff1a;https://video-diffusion.github.io/ paper:https://arxiv.org/pdf/2204.03458 参考:https://zhuanlan.zhihu.com/p/585009571 视频生成方面的扩散模型 论文Video Diffusion Models精读&#xff0c;笔者会多多更新AIGC相关知识&#xff01;点个关注吧&…

简单高效的在线拾色器工具 - 为您的设计工作提供便利

你是否曾在寻找完美颜色时感到困扰&#xff1f;现在&#xff0c;有了这个简单而强大的在线拾色器工具&#xff0c;选择理想的颜色变得轻而易举&#xff01; 网址&#xff1a; https://potatoh5games.fun/tools/#ColorPickr 这款拾色器工具具有以下特点&#xff1a; 用户友好…

外卖项目day09---微信登录/HttpClient

这里是初学的文章&#xff0c;大家可以去看看 HttpClient初学-CSDN博客 下面是微信登录的流程 微信登录产品原型 UserController代码 RestController RequestMapping("/user/user") Api(tags "C端用户相关接口") Slf4j public class UserController {A…

web以及nginx

⼀、web基本概念和常识 Web&#xff1a;为⽤户提供的⼀种在互联⽹上浏览信息的服务&#xff0c;Web 服务是动态的、可交互的、跨平台的和图形化的。 Web 服务为⽤户提供各种互联⽹服务&#xff0c;这些服务包括信息浏览服务&#xff0c;以及各种交互式服务&#xff0c;包括聊天…

【Python数值分析】革命:引领【数学建模】新时代的插值与拟合前沿技术

目录 ​编辑 第一部分&#xff1a;插值的基本原理及应用 1. 插值的基本原理 1.1 插值多项式 1.2 拉格朗日插值 1.3 牛顿插值 1.4 样条插值 2. 插值的Python实现 2.1 使用 NumPy 进行插值 2.2 使用 SciPy 进行插值 2.2.1 一维插值 ​编辑 2.2.2 二维插值 3. 插值…

如何让网站实现https访问

要让网站实现HTTPS访问&#xff0c;主要需要完成以下几个步骤。这些步骤确保了网站与用户之间的数据传输安全&#xff0c;并提升了用户对网站的信任度。 1. 确定证书类型 首先&#xff0c;根据网站的需求和预算&#xff0c;选择合适的SSL证书类型。常见的SSL证书类型包括&…

黑马头条Day11- 实时计算热点文章、KafkaStream

一、今日内容 1. 定时计算与实时计算 2. 今日内容 KafkaStream 什么是流式计算KafkaStream概述KafkaStream入门案例SpringBoot集成KafkaStream 实时计算 用户行为发送消息KafkaStream聚合处理消息更新文章行为数量替换热点文章数据 二、实时流式计算 1. 概念 一般流式计…

Skim通过Apple Script为选中文本添加特定颜色的高亮

一、代码编写 Skim的Apple Script维基页面 使用Mac的Script Editor编写以下代码&#xff1a; tell application "Skim"activatetell document 1set theSel to (get selection)set theNote to make note with data theSel with properties {type:highlight note, co…

Swift学习入门,新手小白看过来

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

零代码拖拽,轻松搞定GIS场景编辑

在三维GIS领域&#xff0c;编辑场景和处理影像数据通常是一个复杂且费时的过程&#xff0c;但现在有了山海鲸可视化&#xff0c;这一切都变得简单有趣。这款免费可视化工具为您提供了零代码拖拽式编辑的体验&#xff0c;让您无需编程知识就能轻松创建和优化GIS场景。通过直观的…