【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

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签