【Linux】命名管道&命名管道和匿名管道的对比&命令行中的管道_程序中使用管道,跟命令行中管道一样吗-程序员宅基地

技术标签: Linux  运维  linux  服务器  

命名管道

匿名管道只能用于具有共同祖先的进程(具有亲缘关系的进程)之间的通信

  • 匿名管道是通过子进程继承父进程的所打开的文件,从而获取到该文件的内核缓冲区作为通信资源

如果要实现两个毫不相关进程之间的通信, 所以引入了命名管道,其可以在两个不相关进程进行通信

命名管道就是一种特殊类型的文件,两个进程通过指定文件路径来打开同一个文件,此时这两个进程也就看到了同一份资源,进而就可以进行通信了


在程序中创建命名管道使用mkfifo函数:

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

image-20220802200408959

参数解析:

第一个参数pathname:表示要创建的命名管道文件

  • 若pathname以路径的方式给出,则将命名管道文件创建在pathname路径下
  • 若pathname以文件名的方式给出,则将命名管道文件默认创建在当前路径下

第二个参数mode_t mode: 表示创建命名管道文件的默认权限

注意:如果我们想将mode设置为666:

image-20220803213026407

实际上,创建出来文件的权限值还会受到umask(文件默认掩码)的影响,实际创建出来文件的权限为:mode&(~umask) ,其中umask的默认值为0002,所以我们设置mode值为0666时实际创建出来文件的权限为0664


若想创建的命名管道文件的权限值不受umask的影响,则要在创建文件前使用 umask 函数将文件默认掩码设置为0

#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);

umask也是系统调用,可以在程序创建文件的时候,指定程序上下文环境的umask,而不影响系统的umask

image-20220803213253583

返回值:

如果管道文件存在,再创建就会报错,这样可以保证管道文件为最新的, 命名管道创建成功返回0, 创建失败返回-1


小例子:利用命名管道进行命令行通信

这里写一个脚本往管道写入数据:

while :; do echo "hello Mango"; sleep 1; done > fifo
image-20220804103559028

左边的进程A用shell脚本每秒向命名管道写入一个字符串,右边的进程B,用cat命令从命名管道当中进行读取

现象就是当进程A启动后,进程B会每秒从命名管道中读取一个字符串打印到显示器上,也就是进程间通信

image-20220804103618094

当管道的读端进程退出后,写端进程再向管道写入数据就没有意义了,此时写端进程会被操作系统杀掉,在这里就可以很好的得到验证 :

当我们终止掉读端进程后,因为写端执行的循环脚本是由命令行解释器bash执行的,所以此时bash就会被操作系统杀掉,于是我们的云服务器也就退出了


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

匿名管道是借助了子进程对父进程的继承性让两个进程看到同一份资源 . 命名管道是通过 路径/文件名的方式定位 唯一的磁盘文件

这里我们希望让server.c 和 client.c这两个进程进行相互通信, 所以我们先写一个Makefile方便我们后序编译


注意:这里我们需要让Makefile一次帮我们生成两个可执行程序,

Makefile自顶向下扫描,默认只会生成第一个目标文件, 所以如果要一次性生成两个可执行程序, 就需要定义一个伪目标:.PHONY:all,并添加对应的依赖关系

.PHONY:all	#定义一个伪目标,依赖的是client和server,  没有依赖方法
all:server client

server:server.c
	gcc -o $@ $^
client:client.c
	gcc -o $@ $^

.PHONY:clean
clean:
	rm -rf client server fifo	#注意这里要把我们创建的管道fifo也删了,不然再次运行的时候会创建失败!

解析:

这样Makefile先看到的是all这个伪目标,要找client 和 server,如果没有就会根据client 和 server的依赖关系和依赖方法分别形成client 和 server, 形成之后,因为all没有依赖方法,最终什么都不做,这样就形成了client和server两个可执行程序!s


我们只需要通信双方进行 文件操作通信即可,

comment.h

对于如何让客户端和服务端使用同一个命名管道文件,这里我们可以让客户端和服务端包含同一个头文件,该头文件当中提供这个共用的命名管道文件的文件名,这样客户端和服务端就可以通过这个文件名,打开同一个命名管道文件,进而进行通信了

用于存放通信双方共有的头文件, comment.h也是两个程序能够看到同一份资源

#pragma once
#include<stdio.h>
#include<sys/stat.h>
#include<sys/stat.h> 
#include<sys/types.h> 
#include<fcntl.h> 
#include<unistd.h> 

#define MY_FIFO "./fifo"  //管道的创建路径
server.c

要干的事情: 创建管道, 读取client发送的信息,并实现对应的业务逻辑

#include"comment.h"

int main()
{
    
    umask(0);将文件默认掩码设置为0
    if(mkfifo(MY_FIFO,0666)<0)  //创建管道
    {
    
        perror("mkfifo:");
        return 1;
    }
    int fd = open(MY_FIFO,O_RDONLY);//以读方式打开命名管道文件
    if(fd<0)
    {
    
        perror("open");
        return 2;
    }

    //处理业务逻辑,进行相应的读写
    while(1)
    {
    
        char buffer[64] = {
    0};
        ssize_t s =  read(fd,buffer,sizeof(buffer)-1);//少读一个字节,防止读满了之后在末尾置\0越界
        if(s > 0)
        {
    
            //读取成功
            buffer[s] = 0;//字符串末尾置\0
            printf("client send: %s\n",buffer);
        }
        else if(s == 0)
        {
    
            //对端关闭了
            printf("client close\n");
            break;
        }
        else
        {
    
            //读取错误
            perror("read:");
            break;
        }
    }
    close(fd);
    return 0;
}

client.c

因为我们在server.c文件中已经创建了管道了,所以此时不需要再创建管道, 只需要获取已经打开的管道文件即可!

client.c需要干的事情: 从键盘中读取数据, 然后把数据发送 ->即:向管道中写入数据

#include"comment.h"
#include<string.h>    //strlen函数
int main()
{
    
    //此时不需要创建管道,只需要获取即可
    int fd = open(MY_FIFO,O_WRONLY);//以写方式打开命名管道文件
    if(fd<0)
    {
    
        perror("open:");
        return 1;
    }
    
    //处理业务逻辑
    while(1)
    {
    
        char buffer[64] = {
    0};
        //1.先从键盘读取内容
        printf("enter Message: ");
        fflush(stdout);//刷新缓冲区
        ssize_t s = read(0,buffer,sizeof(buffer)-1);//从标准输入读取内容,少读一个字节,防止读满了!
        if(s > 0)
        {
    
            buffer[s -1]= 0;//提前置\0,把\n覆盖掉
            printf("%s\n",buffer);//把我们从键盘获取到的数据打印出来看一下
            //向管道中写入数据
            write(fd,buffer,strlen(buffer));
        }
    }
    close(fd);
    return 0;
}

注意:因为系统接口函数write会把回车也认为是读取到的内容,所以我们在键盘中读取完之后, 在字符串末尾把\n给覆盖掉!


效果展示

注意:实现服务端(server)和客户端(client)之间的通信之前,我们需要先让服务端运行起来,让服务端运行后创建一个命名管道文件,然后再以读的方式打开该命名管道文件,之后服务端就可以从该命名管道当中读取客户端发来的通信信息了

image-20220803215949038

case


除此之外,我们还可以让client 控制 server来执行一些事情, 这也是进程通信的一个目的!

补充server.c的业务逻辑: 如果client发送的信息是"show-ls-l" :就执行ls -l命令 如果client发送的信息是"show-train" :就执行sl命令,否则正常打印client发送的内容

#include"comment.h"
#include<string.h>  //strcmp函数
#include<stdlib.h>  //exit函数
#include<sys/wait.h>    //waitpid函数
int main()
{
    
    umask(0);//清空权限掩码
    if(mkfifo(MY_FIFO,0666)<0)  //创建管道
    {
    
        perror("mkfifo:");
        return 1;
    }
    int fd = open(MY_FIFO,O_RDONLY);//以读方式打开
    if(fd<0)
    {
    
        perror("open");
        return 2;
    }

    //处理业务逻辑,进行相应的读写
    while(1)
    {
    
        char buffer[64] = {
    0};
        ssize_t s =  read(fd,buffer,sizeof(buffer)-1);//少读一个字节,防止读满了之后在末尾置\0越界
        if(s > 0)
        {
    
            //读取成功
            buffer[s] = 0;//字符串末尾置\0
            if(strcmp(buffer,"show-ls-l") == 0)
            {
    
                if(fork() == 0)//创建子进程帮我们干活
                {
    
                    execl("/usr/bin/ls","ls","-l",NULL);//进程替换
                    exit(1);//进程替换成功之后是不会走到这里的
                }
                waitpid(-1,NULL,0);
            }
            else if(strcmp(buffer,"show-train") == 0 )   //小火车
            {
    
                if(fork() == 0)
                {
    
                    execl("/usr/bin/sl","sl",NULL);
                    exit(1);
                }
                waitpid(-1,NULL,0);
            }
            else
            {
    
                printf("client send:%s\n",buffer);
            }
        }
        else if(s == 0)
        {
    
            //对端关闭了
            printf("client close\n");
            break;
        }
        else
        {
    
            //读取错误
            perror("read:");
            break;
        }
    }
    close(fd);
    return 0;
}

如果我们让server.c不读取,休眠100s,验证管道的数据会不会刷新到磁盘:

image-20220803222708620

我们可以发现:为了效率, 管道的数据并不会刷新到磁盘!,即 通信是在内存当中进行的


服务端和客户端之间的退出关系

case1: 当客户端退出后,服务端将管道当中的数据读完后就再也读不到数据了,那么此时服务端也就会去执行它的其他代码了(在当前代码中是直接退出了)

image-20220804104730412

case2:当服务端退出后,客户端写入管道的数据就不会被读取了,也就没有意义了,那么当客户端下一次再向管道写入数据时,就会收到操作系统发来的13号信号(SIGPIPE),此时客户端就被操作系统强制杀掉了

image-20220804104829828


命名管道和匿名管道的对比

1)匿名管道由pipe函数创建并打开,命名管道由mkfifo函数创建,由open函数打开

2)为什么pipe叫做匿名管道,而fifo叫做命名管道呢?

  • 匿名管道文件不需要名字,因为它是通过父子继承的方式看到同一份资源, 命名管道一定要有名字, 从而使不相关进程定位同一个文件

3)命名管道和匿名管道一样,都是内存文件, 命名管道和匿名管道都不会将通信数据刷新到磁盘当中


命名管道也是基于字节流的, 所以进程通信的时候需要双方定制通信协议


命令行中的管道

在命令行当中的管道(“|”)到底是匿名管道还是命名管道呢

image-20220804105426260

由于匿名管道只能用于有亲缘关系的进程之间的通信,而命名管道可以用于两个毫不相关的进程之间的通信,因此我们可以先看看命令行当中用管道(“|”)连接起来的各个进程之间是否具有亲缘关系

下面我们通过管道| 连接3个进程: 通过ps命令可以查看这3个进程的信息

image-20220804105818828

这三个进程的PPID是相同的,也就是说它们是由同一个父进程创建的子进程,而它们的父进程实际上就是命令行解释器, Linux下这里为 bash

image-20220804105848434


也就是说,由管道(“|”)连接起来的各个进程是有亲缘关系的,它们之间互为兄弟进程

总结:若是两个进程之间采用的是命名管道,那么在磁盘上必须有一个对应的命名管道文件名,而实际上我们在使用命令的时候并不存在类似的命名管道文件名,因此命令行上的管道实际上是匿名管道

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/chuxinchangcun/article/details/129287293

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文