【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;
}


相关文章
|
18天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
52 4
linux进程管理万字详解!!!
|
9天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
48 8
|
6天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
18天前
|
存储 Unix Linux
进程间通信方式-----管道通信
【10月更文挑战第29天】管道通信是一种重要的进程间通信机制,它为进程间的数据传输和同步提供了一种简单有效的方法。通过合理地使用管道通信,可以实现不同进程之间的协作,提高系统的整体性能和效率。
|
18天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
52 4
|
19天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
5月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
127 13
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
172 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
3月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。