【Linux】进程间通信 -- 命名管道 | mkfifo调用

这篇具有很好参考价值的文章主要介绍了【Linux】进程间通信 -- 命名管道 | mkfifo调用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

小实验

1.我们首先创建两个文件

client.cpp:

#include <iostream>
using namespace std;
int main()
{
    cout<<"hello client"<<endl;
    return 0;
}

server.cpp:

#include <iostream>
using namespace std;
int main()
{
    cout<<"hello server"<<endl;
    return 0;
}

然后创建Makefile使得我们更方便的去编译:

.PHONY:all
all:server client

server:server.cpp
	g++ -o $@ $^ -std=c++11
client:client.cpp
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f server client

这样我们使用一条指令编译两个文件:
【Linux】进程间通信 -- 命名管道 | mkfifo调用,Linux基础,linux,进程间通信,命名管道,mkfifo

2.我们使用创建命名管道的命令mkfifo

【Linux】进程间通信 -- 命名管道 | mkfifo调用,Linux基础,linux,进程间通信,命名管道,mkfifo

[AMY@VM-12-15-centos lesson_15]$ mkfifo named_pipe
[AMY@VM-12-15-centos lesson_15]$ ll -i
total 12
664845 -rw-rw-r-- 1 AMY AMY 102 Jun 29 15:17 client.cpp
664846 -rw-rw-r-- 1 AMY AMY 157 Jun 29 15:19 Makefile
658324 prw-rw-r-- 1 AMY AMY   0 Jun 29 15:29 named_pipe
664847 -rw-rw-r-- 1 AMY AMY 102 Jun 29 15:17 server.cpp
[AMY@VM-12-15-centos lesson_15]$ 

我们可以发现我们创建的named_pipe是以p开头而且有自己独立的inode,说明它是一个独立的管道文件

3.开始执行

我们执行下面脚本,主要的功能就是使用echo循环输出hello world!到管道文件named_pipe

cnt=0; while :; do echo "hello world! -> $cnt"; let cnt++; sleep 1; done > named_pipe

【Linux】进程间通信 -- 命名管道 | mkfifo调用,Linux基础,linux,进程间通信,命名管道,mkfifo
我们发现,我执行输出到管道文件,但是管道文件里面的文件大小始终没有变化且为0

我们再使用cat命令将管道文件named_pipe的内容,重定向输出到显示器,能看到脚本循环输出的值,且是从0开始的
【Linux】进程间通信 -- 命名管道 | mkfifo调用,Linux基础,linux,进程间通信,命名管道,mkfifo

命名管道是如何做到让不同的进程,先看到了同一份资源?可以让不同的进程打开指定名称(路径+文件名)的同一个文件
原因是:路径+文件名=唯一性(匿名管道是通过继承)

在语言层面使用命名管道实现通信

1.创建命名管道-函数mkfifo

【Linux】进程间通信 -- 命名管道 | mkfifo调用,Linux基础,linux,进程间通信,命名管道,mkfifo
comm.hpp创建一个管道文件

#pragma once

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

#define NAMED_PIPE "/tmp/my_named_pipe"
//创建指定路径上的命名管道
bool createFifo(const std::string &path)
{
    umask(0);//将权限掩码设置为0
    int n = mkfifo(path.c_str(), 0600);
    if (n == 0)
        return true;
    else
    {
        std::cout << "errno: " << errno << " err string: " << strerror(errno) << std::endl;
        return false;
    }
}
//删除指定路径上的命名管道
void removeFifo(const std::string &path)
{
    int n = unlink(path.c_str());
    assert(n == 0);
    (void)n;
}

解释:函数createFifo的作用是创建一个匿名管道,而它传入的参数path则是创建的命名管道的路径。函数调用mkfifo(path.c_str(), 0600)来创建一个命名管道。mkfifo()是一个系统调用,用于创建一个新的命名管道。它的第一个参数是一个指向管道路径的字符串,第二个参数是一个表示权限位的八进制数。在这个例子中,权限被设置为"0600",表示只有管道的所有者可以读写,其他用户没有任何访问权限。

返回值:如果mkfifo()调用成功,它将返回0,函数将返回true表示管道创建成功。如果mkfifo()调用失败,它将返回-1,并设置全局变量errno来指示错误的类型。函数将打印错误信息并返回false表示管道创建失败。strerror(errno)用来获取对应错误码的错误信息。

2.写入端代码

client.cpp:

#include "comm.hpp"
int main()
{
    std::cout << "client begin" << std::endl;
    int wfd = open(NAMED_PIPE, O_WRONLY);//仅以写入模式打开
    std::cout << "client end" << std::endl;
    if(wfd < 0) exit(1); 

    //write
    char buffer[1024];
    while(true)
    {
        std::cout << "Please Say# ";
        fgets(buffer, sizeof(buffer), stdin); // abcd\n
        if(strlen(buffer) > 0) buffer[strlen(buffer)-1] = 0;
        ssize_t n = write(wfd, buffer, strlen(buffer));
        assert(n == strlen(buffer));
        (void)n;
    }

    close(wfd);
    return 0;
}

代码功能:从标准输入stdin读取用户输入,并将其写入到一个命名管道中
解释:
fgets(buffer, sizeof(buffer), stdin);用于从标准输入读取一行文本。fgets()是一个标准库函数,用于从指定的文件流中读取一行文本。它的第一个参数是一个字符数组,用于存储读取的文本,第二个参数是数组的长度,第三个参数是要读取的文件流。在本例中,fgets()将从标准输入(stdin)中读取一行文本,并将其存储到buffer数组中。

if(strlen(buffer) > 0) buffer[strlen(buffer)-1] = 0;用于去掉输入文本中的换行符\n,因为fgets()函数会将输入文本中的换行符也读取进来。如果输入文本的长度大于0,则将最后一个字符设为0,以去掉换行符。

ssize_t n = write(wfd, buffer, strlen(buffer));用于将去掉换行符的输入文本写入到命名管道中。write()是一个系统调用,用于向文件描述符中写入数据。它的第一个参数是文件描述符,第二个参数是要写入的数据,第三个参数是要写入的数据长度。在本例中,wfd是命名管道的文件描述符,buffer是要写入的数据,strlen(buffer)是要写入的数据长度。

assert(n == strlen(buffer));用于确保写入操作成功,并且写入的数据长度与预期的长度相同。assert()是一个断言宏,用于在条件不满足时引发一个断言错误。在本例中,如果写入操作失败或者写入的数据长度与预期的长度不同,assert()将引发一个断言错误,并停止程序的执行。(void)n;用于避免编译器警告未使用变量n的情况。(因为Linux默认编译是release版本的,而release版本的assert会失效)

3.读取端代码

server.cpp:

#include "comm.hpp"

int main()
{
	//创建管道并检查是否创建好
    bool r = createFifo(NAMED_PIPE);
    assert(r);
    (void)r;

    std::cout << "server begin" << std::endl;
    int rfd = open(NAMED_PIPE, O_RDONLY);//打开文件以只读方式
    std::cout << "server end" << std::endl;
    if(rfd < 0) exit(1);

    //read
    char buffer[1024];
    while(true)
    {
        ssize_t s = read(rfd, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;
            std::cout << "client->server# " << buffer << std::endl;
        }
        else if(s == 0)
        {
            std::cout << "client quit, me too!" << std::endl;
            break;
        }
        else
        {
            std::cout << "err string: " << strerror(errno) << std::endl;
            break;
        }
    }

    close(rfd);
    removeFifo(NAMED_PIPE);
    return 0;
}

代码功能:使用一个无限循环,从一个命名管道中读取数据,并将读取到的数据打印到标准输出stdout
解释:
ssize_t s = read(rfd, buffer, sizeof(buffer)-1);用于从命名管道中读取数据。read()是一个系统调用,用于从文件描述符中读取数据。它的第一个参数是文件描述符,第二个参数是用于存储读取数据的缓冲区,第三个参数是缓冲区的长度。在本例中,rfd是命名管道的文件描述符,buffer是用于存储读取数据的缓冲区,sizeof(buffer)-1是缓冲区的长度。

if(s > 0)用于判断是否成功读取到数据。如果s大于0,则说明成功读取到数据。buffer[s] = 0;用于将读取到的数据转换为字符串,并在字符串末尾添加一个空字符,以便将其打印到标准输出上。std::cout << "client->server# " << buffer << std::endl;用于将读取到的数据打印到标准输出上。

else if(s == 0)用于判断是否已经读取到了命名管道的末尾。如果s等于0,则说明已经读取到了命名管道的末尾,即客户端已经关闭了管道。此时程序将打印一条消息并退出循环。

else用于处理读取数据时发生的错误。如果s小于0,则说明读取数据时发生了错误。strerror(errno)用于获取错误信息,并将其打印到标准输出上。break用于退出循环。

小细节:

    std::cout << "server begin" << std::endl;
    int rfd = open(NAMED_PIPE, O_RDONLY);
    std::cout << "server end" << std::endl;

【Linux】进程间通信 -- 命名管道 | mkfifo调用,Linux基础,linux,进程间通信,命名管道,mkfifo
【Linux】进程间通信 -- 命名管道 | mkfifo调用,Linux基础,linux,进程间通信,命名管道,mkfifo
【Linux】进程间通信 -- 命名管道 | mkfifo调用,Linux基础,linux,进程间通信,命名管道,mkfifo

我们发现./server运行后输出"server begin"之后到open阻塞了,而是要等./client后才会继续执行,因为读取端打开要等写入端也打开才会继续运行,也就是说两者都要同时打开才能运行这个管道


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀文章来源地址https://www.toymoban.com/news/detail-541285.html

到了这里,关于【Linux】进程间通信 -- 命名管道 | mkfifo调用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux——进程间通信——命名管道

     ✅1主页::我的代码爱吃辣 📃2知识讲解:Linux——进程间通信——命名管道 ☂️3开发环境:Centos7 💬4前言:命名管道是一种特殊的文件存放在文件系统中,而不是像管道那样存放在内核中。命名管道可以用于任何两个进程间的通信,而不限于同源的两个进程。当进程对

    2024年02月08日
    浏览(37)
  • Linux进程间通信【命名管道】

    ✨个人主页: 北 海 🎉所属专栏: Linux学习之旅 🎃操作环境: CentOS 7.6 阿里云远程服务器 命名管道通信属于 IPC 的其中一种方式,作为管道家族,命名管道的特点就是 自带同步与互斥机制、数据单向流通 ,与匿名管道不同的是:命名管道有自己的名字,因此可以被没有血

    2024年02月08日
    浏览(26)
  • 【Linux】进程间通信(匿名管道 & 命名管道)-- 详解

    如何理解进程间通信? 进程具有独立性,所以进程想要通信难度是比较大的,成本高。 在日常生活中,通信的本质是传递信息,但站在程序员角度来看, 进程间通信的本质:让不同的进程看到同一份资源(内存空间) 。 进程间通信就是进程之间互相传递数据,那么进程间

    2024年04月28日
    浏览(36)
  • Linux通信--构建进程通信的 方案之管道(下)|使用匿名管道实现功能解耦|命名管道实现serve&client通信

    文章目录 一、管道的应用实例-父进程唤醒子进程,子进程执行某种任务 二、命名管道 1.创建一个命名管道 2.匿名管道与命名管道的区别 3.命名管道的打开规则 4.用命名管道实现serverclient通信 后续将源码上传到gitee,上传后修改链接。 管道应用的一个限制就是只能具有共同祖

    2024年02月10日
    浏览(32)
  • 【看表情包学Linux】IPC 进程间通信 | PIPE 管道 | 匿名管道 | 管道通信的原理 | 系统调用: pipe 接口

       🤣  爆笑 教程  👉 《看表情包学Linux》 🔥 CSDN 累计订阅量破千的火爆 C/C++ 教程的 2023 重制版,C 语言入门到实践的精品级趣味教程。 了解更多: 👉  \\\"不太正经\\\" 的专栏介绍  ← 试读第一章 订阅链接: 🔗 《C语言趣味教程》 ← 猛戳订阅! 目录 Ⅰ. 进程间通信(I

    2024年02月14日
    浏览(28)
  • 【Linux从入门到精通】通信 | 管道通信(匿名管道 & 命名管道)

        本派你文章主要是对进程通信进行详解。主要内容是介绍 为什么通信、怎么进行通信。其中本篇文章主要讲解的是管道通信。希望本篇文章会对你有所帮助。 文章目录 一、进程通信简单介绍 1、1 什么是进程通信 1、2 为什么要进行通信  1、3 进程通信的方式 二、匿名管

    2024年02月09日
    浏览(35)
  • 【Linux】匿名管道与命名管道,进程池的简易实现

    本质是先让不同的进程看到同一份资源,也就是两个进程都能对管道文件的缓冲区进行操作 这里我们pipe的时候,会使用两个文件描述符,这两个文件描述里面存的file结构体是同一个,也就是管道文件的file结构体,file结构体中存储有inode以及系统缓冲区,此时fork一个子进程

    2024年02月05日
    浏览(31)
  • 进程间通信-命名管道

            先前已经了解了匿名管道,但是这是适用于有血缘关系的进程间,如果无血缘关系的进程要实现通信, 此时需要有另一种通信方案-命名管道。为什么命名管道可以用于无血缘关系的进程间通信,什么是命名管道,为什么说它是有名字的,后面我们会一一了解。

    2024年01月20日
    浏览(25)
  • 进程间通信(命名管道)

    目录:            1.命名管道            2.创建命名管道 --------------------------------------------------------------------------------------------------------------------------------- 1.命名管道 1.管道的一个应用限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信 2.如果我们想在不相

    2024年02月06日
    浏览(25)
  • 进程间通信之利用命名管道进行通信

    命名管道(Named Pipe),也被称为FIFO(First In, First Out),是一种在Unix和Unix-like操作系统中用于进程间通信的特殊文件类型。它允许不相关的进程通过文件系统中的路径名进行通信。 命名管道(Named Pipe)是一种在Unix和Unix-like系统中用于进程间通信的特殊文件类型。它的作用主

    2024年01月19日
    浏览(24)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包