【Linux】进程间通信——命名管道

简介: 【Linux】进程间通信——命名管道

命名管道

匿名管道只能用来进行进程间通信,让具有血缘关系的进程进行通信

让毫不相关的进程之间进行通信,就需要采用命名管道通信


因为该文件有文件名称的,而且必须要有,所以叫做命名管道


1. 见一见管道文件

mkfifo函数

输入 man mkfifo 指令


b1daa3aac7f343c0ac00fdef7b9ca961.png


制作一个 FIFOS ,表示命名管道

bcf8c519e5a44438b02f06c810927fe7.png


mkfifo fifo 制作一个管道 ,并命名为 fifo

文件类型以p开头,被称为管道文件


输入 man 3 mkfifo 指令


5772720013b94f9dbb7c8f08437d0a2b.png

pathname代表路径,若不带路径只有文件名,默认在当前路径下

mode代表创建权限的模式 ,即创建文件的权限(666、664)

成功返回0,失败返回-1


管道文件的使用


将hello world 重定向到fifo管道中

但是好像并不会写入

fifo只代表一种符号,向符号写入消息并不会刷新到磁盘上,而是只会把hello world写到管道中

但是管道文件是内存文件,所以大小不会改变


通过赋值SSH渠道,创建终端2

在保证终端1的输出重定向 运行的情况下

cat默认从显示器中读取

在终端2中 使用输入重定向 将 fifo重定向到显示器中


2ee73463a5124bcf80187156defbba54.png

最终在终端2中显示 hello world

而实际上 输出重定向和输入重定向 的启动都是进程,并且毫不相关


2. 命名管道原理


1aeca598e08f42f08ff7a30714555e0c.png

要打开对应的文件,就会在操作系统内创建struct file对象,struct file对象有自己的缓冲区

由于0 1 2 分别被占用,所以3指向struct file对象


若有一个毫不相关的进程,也打开磁盘中的文件,操作系统内部就不会再创建struct file对象,

会直接把struct file对象的地址填入新建立进程对应的下标里

在struct file对象中存在一个引用计数默认为1 ,当新创建一个进程时,引用计数就会变成2

此时两个进程指向同一份文件


目的是让两个进程之间进行通信,所以就不应该把数据刷到磁盘上,

应该把磁盘文件改为内存级的,不会进行刷盘,把它命名为管道文件


如何保证两个毫不相关的进程,看到的是同一个文件,并打开?

文件的唯一性,使用路径表示的

让不同的进程通过文件路径+文件名看到同一个文件,并打开,就是看到了同一个资源


3. 用命名管道实现server&client通信


在vscode中,分别创建server.cc文件和client.cc文件以及makefile


如何使用makefile连续生成可执行程序

13fd0939ae584167a9a69cb71f33313d.png

若这样创建makefile,只会执行server可执行程序

server是从上到下扫描遇到的第一个真正的目标文件

makefile从上到下扫描时,会默认执行第一组依赖关系和依赖方法


为了不让client和server成为目标文件


450e7eef68464437b393b147c11cd07b.png

这样就可以一次生成两个可执行程序了


comm.hpp文件

建立一个公共头文件 comm.hpp,在内部创建公共的路径以及mode

(以hpp结尾.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件)

#pragma once
#include<iostream>
#include<string>
#define NUM 1024
using namespace std;
const string fifoname="./fifo";//管道名字为当前路径创建的fifo
mode_t mode=0666;//默认权限为0666

这样 server文件和client文件就会调用同一份文件路径了


server.cc 服务端

1. 创建一个管道文件

创建server.cc文件,使用mkfifo函数创建管道文件


5dbaa2d2a36a4572ba63ade4247e524e.png

2475bb6ce51640e383b975971e0b8dc4.png

此时运行可执行程序,即可生成fifo管道文件


2d992311163c4873b8eebeb1fdd8e267.png


权限变为664 ,可是在comm.hpp中设置的权限为666

mode最终是要与umask进行操作的


f1362977a3aa46d9827eef07bbe7dc01.png


手动将掩码置为0后,即可解决权限被修改的问题


手动删除fifo后,再次运行


f2fb151c508841049e9c839b0d5d98af.png


此时权限还是666,没有被修改


2. 让读写端进程分别按照自己的需求打开文件

ea37f1588e75402f9cb591ca127cc4a0.png

将文件描述符内容打印到buffer中

分为三种情况

若返回>0,则读取成功,而系统并不知道buffer是一个字符串,而我们自己知道,所以要在结尾加上\0

若返回==0,说明读到文件结尾,当写端关闭时,读端才会读到文件结尾

若返回<0,说明读取失败,则返回错误码

3. 整体代码

//服务端
#include<iostream>
using namespace std;
#include<sys/stat.h>
#include<sys/types.h>
#include<cerrno>
#include<cstring>
#include<fcntl.h>
#include<unistd.h>
#include"comm.hpp"//公共路径
int main()
{
    umask(0);//将当前进程的umask掩码设为0
  //创建管道文件,只需要创建一次
  int n=mkfifo(fifoname.c_str(),mode);
  if(n!=0)//创建失败
  {
    //失败就返回错误码
   cout<<errno<<":"<<strerror(errno)<<endl;
   return 1;
  }
   cout<<"create fifo success"<<endl;
   //2.让服务端直接开启管道文件
   int rfd=open(fifoname.c_str(),O_RDONLY);
   //第二个参数代表读
   //以读方式打开文件
   if(rfd<0)//创建失败
   {
     //失败就返回错误码
     cout<<errno<<":"<<strerror(errno)<<endl;
     return 2;
   }
   cout<<"open fifo success,begin"<<endl;
   // 3.正常通信
   char buffer[NUM];
   while(true)
   {
      buffer[0]=0;
    //rfd作为文件描述符(0/1/2)
      ssize_t n=read(rfd,buffer,sizeof(buffer)-1);//将rfd的内容读到buffer中
      if(n>0)//读取成功
      {
       buffer[n]='\0';
       cout<<"client#"<<buffer<<endl;
      }
      else if(n==0)//读到文件结尾为0
      {
          //写端关闭
          cout<<    "cilent quit,me too"<<endl;
          break;
      }
      else //读取失败
      {
         cout<<errno<<":"<<strerror(errno)<<endl;
         break;
      }
   }
   //关闭不要的fd
  close(rfd);
  unlink(fifoname.c_str());//删除文件fifo
    return 0;
}

client.cc 客户端

由于在服务端创建了管道文件,所以在客户端不用创建管道文件


bb8a014837424d5fafbcc46f4e090609.png

直接打开文件即可,以写方式打开文件


fe1f3858df8746f3b7b673b647ab706b.png


为了避免输入的单词有空格存在

输入 man fgets 指令


912dc6ebef66486790cfa36f33e91640.png


从指定的流中获取字符串,并规定字符串的大小


因为有两个可执行程序存在,所以需要两个终端


b2ce6c6104164d4ebc9482a637eb02ae.png


当终端2没有运行server时,没有管道文件存在,而终端1运行server后,终端1中出现管道文件


c6fbadc7a3c24898bb0a780a474757cf.png


当终端1运行client时,输入对应的信息,终端2中会自动显示出来

client端可以将信息发送给server端


完整代码

//客户端
#include<iostream>
using namespace std;
#include<sys/stat.h>
#include<sys/types.h>
#include<cerrno>
#include<cstring>
#include<fcntl.h>
#include<unistd.h>
#include<cstdio>
#include<cassert>
#include"comm.hpp"//公共路径
using namespace std;
int main()
{
   //不需要创建管道文件,打开文件即可
   int wfd=open(fifoname.c_str(),O_WRONLY);//以写的方式打开文件
   if(wfd<0)//说明打开失败
   {
     cout<<errno<<":"<<strerror(errno)<<endl;
     return 0;
   }
   //进行常规通信
   char buffer[NUM];
    while(true)
    {
        cout<<"请输入你的消息# ";
        char *msg=fgets(buffer,sizeof(buffer)-1,stdin);//将标准输入流的数据写入buffer中
        assert(msg);//检查是否为空
        (void)msg;//保证rlease模式发布依旧被使用
        //fgets会读取回车 即\n
        buffer[strlen(buffer)-1]=0;//12345\n  把\n位置覆盖为\0
        ssize_t n=write(wfd,buffer,strlen(buffer));
        assert(n>=0);
    }
   close(wfd);//关闭文件描述符
    return 0;
}


相关文章
|
23天前
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
99 2
|
17天前
|
消息中间件 Unix Linux
C语言 多进程编程(二)管道
本文详细介绍了Linux下的进程间通信(IPC),重点讨论了管道通信机制。首先,文章概述了进程间通信的基本概念及重要性,并列举了几种常见的IPC方式。接着深入探讨了管道通信,包括无名管道(匿名管道)和有名管道(命名管道)。无名管道主要用于父子进程间的单向通信,有名管道则可用于任意进程间的通信。文中提供了丰富的示例代码,展示了如何使用`pipe()`和`mkfifo()`函数创建管道,并通过实例演示了如何利用管道进行进程间的消息传递。此外,还分析了管道的特点、优缺点以及如何通过`errno`判断管道是否存在,帮助读者更好地理解和应用管道通信技术。
|
7天前
|
存储 监控 安全
探究Linux操作系统的进程管理机制及其优化策略
本文旨在深入探讨Linux操作系统中的进程管理机制,包括进程调度、内存管理以及I/O管理等核心内容。通过对这些关键组件的分析,我们将揭示它们如何共同工作以提供稳定、高效的计算环境,并讨论可能的优化策略。
15 0
|
19天前
|
Unix Linux
linux中在进程之间传递文件描述符的实现方式
linux中在进程之间传递文件描述符的实现方式
|
20天前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
43 0
|
3月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
|
2月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
2月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
71 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
1月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。
|
2月前
|
存储 缓存 安全
【Linux】冯诺依曼体系结构与操作系统及其进程
【Linux】冯诺依曼体系结构与操作系统及其进程
141 1