Linux操作系统~匿名管道和命名管道的使用及其原理分析

news2024/11/14 23:35:35

目录

1.匿名管道

(1).匿名管道的原理

(2).pipe接口的使用

如果只写不读(求管道的大小)

(3).匿名管道五个特点

(4).匿名管道的四种情况

3.命名管道

(1).命名管道的原理/实质

(2).在命令行中创建使用匿名管道

(3).代码中创建使用命名管道

1).同时编译多个文件,makefile

2).mkfifo()函数

3).e.g.

(4).命名管道注意事项

4.匿名管道和命名管道的区别与联系


1.匿名管道

(1).匿名管道的原理

        1.创建子进程的时候,PCB也是要自己有一份的,同样里面的files_struct也是要自己独立有一份的(这些都属于进程的数据结构,进程是具有独立性的)

        2.而通过文件描述符找到的struct_file则是不需要给子进程拷贝一份的,因为这个是属于文件的,和创建进程没有关系。

        3.调用write方法的时候,是系统调用,会先将数据放在文件的内核缓冲区(不是C语言层面的缓冲区),底层定期的将缓冲区中的内容写到磁盘中。

        可以看出,父进程和子进程可以看到的公共区域是struct_file,这里也就可以进行进程间通信了。struct_file里面有文件缓冲区,一个进程往缓冲区里面写数据,一个进程往缓冲区里面读数据,这就是匿名管道的原理,一种基于文件的通信方式


(2).pipe接口的使用

pipefd[2]:是一个输出性参数!我们想通过这个参数读取到打开的两个fd/

int pipe(int pipefd[ 2]);

pipe[0]表示读取端,pipe[1]表示写入端

e.g.:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

int main()
{
    int pipe_fd[2] = {0};

    if (pipe(pipe_fd) < 0)
    {
        perror("pipe");
        return 1;
    }
    printf("%d, %d\n", pipe_fd[0], pipe_fd[1]); // 输出3,4

    pid_t id = fork();
    if (id < 0)
    {
        perror("fork");
        return 2;
    }
    else if (id == 0)
    { 
        // 让父进程读取,子进程写入
        close(pipe_fd[0]); // 关掉读取端

        char c = 'x';
        int count = 0;

        while (1)
        {
            write(pipe_fd[1], &c, 1);
            // sleep(1);
            count++;
            printf("write: %d\n", count);
        }

        close(pipe_fd[1]);
        exit(0);
    }
    else
    { 
        // 父进程读
        close(pipe_fd[1]); // 关掉写入端

        char buffer[64];
        while (1)
        {
            // sleep(100);让父进程不读,用于模拟计算管道的大小
            sleep(1);
            size_t size = read(pipe_fd[0], buffer, sizeof(buffer) - 1); // 默认都是0,最后留一个位置作为\0
            // 如果read返回值为0,意味着子进程关闭文件描述符了,相当于读到文件结尾了
            if (size > 0)
            {
                buffer[size] = 0;
                printf("parent get messge from child# %s\n", buffer);
            }
            else if (size == 0)
            {
                printf("pipe file close, child quit!\n");
                break;
            }
            else
            {
                break;
            }
        }

        int status = 0;
        if (waitpid(id, &status, 0) > 0)
        {
            printf("child quit, wait success!, sig: %d\n", status & 0x7F);
        }
        close(pipe_fd[0]);
    }
    return 0;
}

 这里是子进程写入,父进程读取,子进程每1s写入一个x,父进程每1s读取一个x。

如果只写不读(求管道的大小)

所以如果我们需要知道管道的大小,写一个程序算一下就行。

这里管道的大小是64KB

        方法:将之前代码中的父进程(读取端)sleep(100),让自进程一直写,最后我们会发现它停止在65536,因为管道满了,写端被阻塞,需要等读端来读

(3).匿名管道五个特点

        1.管道是一个只能单向通信的通信信道,这里父进程有一个读端和一个写端,这样子进程继承下去才可以读写,如果只有一个读端,那么子进程也就只能读了。因为是单向通信,所以父进程一次只能开一个写端或者一个读端,子进程也是。(要么父写子读,要么子写父读)

        2.管道是面向字节流的(也就是读取数据的时候只有字节的概念,你让我读多少个字节就读多少个,具体哪部分有用等读进来到用户层面处理)

        3.管道自带同步机制(写端写满了管道,就不写了,等对方读;读端读完了管道内容,就不读了,等对方写)  

原子性写入/读取(如果写满之后,只读一点点数据,是不能唤醒对方来写的,要读一定的数据量以后,才能唤醒对方来写,这个数据量就是PIPE_BUF。同理只写一点点数据,也无法唤醒对方来读)

  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

        4.仅限于父子间通信(具有血缘关系的进程进行进程间通信)

      5.管道的生命周期是随进程的(进程结束后,管道作为文件会被OS自动关闭,即使没有close)


(4).匿名管道的四种情况

管道的四种情况:

  • 当没有数据可读时

O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止

//O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。(非阻塞式)

  • 当管道满的时候

O_NONBLOCK disable: write调用阻塞,直到有进程读走数据

//O_NONBLOCK enable:调用返回-1,errno值为EAGAIN(非阻塞式)

  • 如果所有管道写端对应的文件描述符被关闭,则read返回0,表明读到文件结尾
  • 如果所有管道读端对应的文件描述符被关闭,则write操作会让OS给目标进程发送信号SIGPIPE,终止写入进程

----(我们可以验证,父进程读,子进程写入;父进程关闭读端,此时操作系统会向进行写操作的进程发出SIGPIPE信号,从而使子进程异常终止。此时父进程通过waitpid获取子进程的status,从status中获取退出码和退出信号)


3.命名管道

为了解决匿名管道只能父子通信,引入了命名管道。

命名管道让两个毫不相关的进程进行通信。

(1).命名管道的原理/实质

进程是具有独立性的

->进程通信的成本其实比较高

->必须先解决一个问题

->让不同的进程先要看到同一份资源(内存文件,内存,队列)[一定需要OS来提供]

->pipe本质:是通过子进程继承父进程资源的特性,达到一个让不同的进程看到同一份资源

        如果是两个不相关的进程,我们要通信可以创建一个文件来做到两个进程间的通信(我们标识一个磁盘文件的时候,可以用路径/文件名来标识,因为操作系统的文件结构是树形结构,每个叶子结点向上追溯到根节点的路径是唯一的)

        A进程把内容写入文件temp.txt,B进程从temp.txt中读取,这就实现了进程间通信。但是这种方法很慢,我们可以将文件放在内存中,不要放在磁盘中,这样就会更加快速,A和B同时打开这个放在内存中的文件

所以我们需要一个文件,

1.它被打开的时候,不会把数据刷新到磁盘里,而是保存在内存中

2.在内存中也有一个文件名,方便两个进程通过路径+文件名看到这个文件。

        这样的文件就是命名管道(命名的意思是必须有名字,为了让两个进程可以通过同一个路径+文件名找到同一个文件)


(2).在命令行中创建使用匿名管道

p表示管道文件

使用mkfifo创建一个命名管道,会出现一个类型为p的文件,也就是我们创建的命名管道

 

 一个进程往命名管道中写入内容,另一个进程可以从管道文件中读取数据,这里写了一个shell脚本

while:; do echo "zebra的数据"; sleep 1; done > namedPipe


(3).代码中创建使用命名管道

1).同时编译多个文件,makefile

all:client server

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

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

.PHONY:clean
clean:
	rm -f client server fifo

 

2).mkfifo()函数

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

filename表示管道的文件名(可以加上路径),mode表示创建的管道文件的权限

        1.在server.c里面调用mkfifo创建命名管道文件,权限设置为0666(注意这里需要设置一下umask,否则会受到系统默认umask的影响,默认是002)

如果创建失败(比如同名管道文件已经存在),就打印出错误信息。

#define MY_FIFO "./fifo"
    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;
    }

        2.一旦我们有了命名管道。此时,我们只需要让通信双方按照文件操作即可(推荐使用系统调用接口,没有用户层缓冲区的问题)

        3.在client.c里面不需要再创建管道文件,直接打开文件开写就行。

        4.因为命名管道也是基于字节流的,所以实际上,信息传递的时候,是需要通信双方定制“协议的”,这里暂时不考虑协议相关问题。

3).e.g.

实现一个让client从键盘读入数据,并向管道中写入内容,让server从命名管道中读取文件内容。

         这里在server里面加了一些处理逻辑,如果server读到的内容是show,就创建子进程执行ls -l命令,如果读到的内容是run,就创建子进程执行sl命令;如果读到的是其他内容,就直接将内容打印出来。

server.c:

#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#define MY_FIFO "./fifo"
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};
        sleep(1);
        ssize_t s = read(fd, buffer, sizeof(buffer)-1); //键盘输入的时候,\n也是输入字符的一部分
        if(s > 0){
            //success
            buffer[s] = 0;
            if(strcmp(buffer, "show") == 0){  //如果输入show,创建子进程执行ls -l命令
                if(fork() == 0){
                    execl("/usr/bin/ls", "ls", "-l", NULL);
                    exit(1);
                }

                waitpid(-1, NULL, 0);
            }
            else if(strcmp(buffer, "run") == 0){  //如果输入run,创建子进程执行sl命令
                if(fork() == 0){
                    execl("/usr/bin/sl", "sl", NULL);
                }
                waitpid(-1, NULL, 0);
            }
            else{
                printf("client# %s\n", buffer);
            }
        }
        else if(s == 0){
            //peer close
            printf("client quit ...\n");
            break;
        }
        else{
            //error
            perror("read");
            break;
        }
    }

    close(fd);
    return 0;
}

client.c:

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

#define MY_FIFO "./fifo"
int main()
{
    //用不用在创建fifo?? 我只要获取即可
    int fd = open(MY_FIFO, O_WRONLY); //不需要O_CREAT
    if(fd < 0){
        perror("open");
        return 1;
    }

    //业务逻辑
    while(1){
        printf("请输入# ");
        fflush(stdout);
        char buffer[64] = {0};
        //先把数据从标准输入拿到我们的client进程内部
        ssize_t s = read(0, buffer, sizeof(buffer)-1);
        if(s > 0){
			// //获取到数据以后,需要把最后一个字符置为\0(‘\0’就是0,这里去掉最后一个字符,并设置为\0)
            buffer[s-1] = 0;
            printf("%s\n", buffer);

            //拿到了数据
            write(fd, buffer, strlen(buffer)); //这里不需要-1,因为是写入数据,操作系统不需要最后/0作为标识符,C语言中的字符串才需要
        }
    }

    close(fd);
    return 0;
}

运行结果:

 


(4).命名管道注意事项

1.我们ls -l可以看到,我们让server进程sleep,然后client进程不断输入内容,但是此时管道文件的大小并没有发生变化,一直是0,因为命名管道的数据不会刷新到磁盘,在内存中放着。(ls指令查看的是磁盘中的文件信息)        

2.命名管道必须要有名字,因为它需要保证两个进程可以通过路径+文件名共同看到同一个文件。

        匿名管道pipe不需要名字,打开两个fd,指向一块内核文件缓冲区用于通信,匿名管道没有文件实体,有名管道有文件实体

        因为匿名管道是通过父子继承的方式,子进程的file_struct里面也会有两个fd,分别指向读端和写端,此时我们只需要关闭一个用一个就行。

3.为什么叫fifo?mkfifo?

因为管道文件是遵循先进先出的原则的,先写入的数据,会被先读取。


4.匿名管道和命名管道的区别与联系

联系:

1.都是基于文件的通信方式

2.两者只能用于数据的单向传输,如果要用命名管道实现两个进程间数据的双向传输,建议使用两个单向的命名管道。(todo??)

3.两者虽然都是基于文件的,但是管道中的内容都不会像普通文件一样保存在磁盘中,读取过的数据就会失效,不能重复读取

区别:

1.匿名管道没有文件实体,命名管道有文件实体(保存在内存中,不会写入磁盘)

2.匿名管道pipe不需要名字,打开两个fd,指向一块内核文件缓冲区用于通信。因为匿名管道是通过父子继承的方式,子进程的file_struct里面也会有两个fd,分别指向读端和写端,此时我们只需要关闭一个用一个就行

3.匿名管道只能用于有血缘关系,也就是有公共祖先的进程通信。命名管道可以用于任意进程间的通信。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/56326.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

世界杯之用Java实现随机胜平负

一、本章猜测随机数首先需要用到Scanner语句&#xff0c;对用户需要几组胜平负数量进行猜测&#xff0c;说动用到几组肯定要用到for循环了&#xff0c;还有要实现随机&#xff0c;就需要用到Math方法&#xff0c;进行随机抽取。 1.Scanner 首先使用Scanner语句抓取用户…

MATLAB算法实战应用案例精讲-【工具篇】运筹优化工具OR-TOOLS(补充篇)(附实战案例及代码实现)

前言 本文为【工具篇】运筹优化工具OR-TOOLS(附实战案例及代码实现)的补充篇。 OR-Tools是一个用于优化的开源软件套件,用于解决车辆路径、流程、整数和线性规划以及约束编程等世界上最棘手的问题。同时OR-Tools提供了C++,Python,Java,.NET的接口,同时提供统一接口封装来…

为你揭秘拼购为什么是破产老板手中的最后一根稻草?

拼购&#xff0c;已经成为了电商平台自主传播的一种营销活动&#xff0c;通过拼团可以促成更多的成交量&#xff0c;但拼团也不是这样简单的放在那里就能有客户进来参与&#xff0c;其中还有很多细节上面的地方需要我们好好探究。这个拼购模式和我们之前见过的拼多多拼团、拼购…

什么蓝牙耳机适合realme手机?适合realme手机的高端蓝牙耳机推荐

根据网络调查数据显示&#xff0c;市面上的耳机需求量在不断增加&#xff0c;随着智能手机的普及&#xff0c;耳机作为炙手可热的产品&#xff0c;尤其是网易云、全民K歌&#xff0c;直播的流行&#xff0c;消费者对于耳机的需求不仅仅是听歌了&#xff0c;有线耳机也逐渐被无线…

【Matplotlib绘制图像大全】(五):饼图

前言 大家好,我是阿光。 本专栏整理了《Matplotlib绘制图像大全》,内包含了各种常见的绘图方法,以及Matplotlib各种内置函数的使用方法,帮助我们快速便捷的绘制出数据图像。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmMatp…

风靡互联网关键词 Web3.0 | 区块链 | 比特币 | 元宇宙……

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Web web是互联网的总称&#xff0c;全称为World Wide Web&#xff0c;缩写WWW &#xff0c;即全球广域网&#xff0c;也称为万维网&#xff0c;它是一种基于超文本和H…

007.复原 IP 地址

1.题目链接&#xff1a; 93. 复原 IP 地址 2.解题思路&#xff1a; 2.1.题目要求&#xff1a; 给定一串只包含数字的字符串s&#xff0c;返回所有让 s 构成 有效IP地址 的数字组合。 有IP地址&#xff1a; 4个 [0,255] 范围内的数字 组成&#xff0c;并且整数之间用 " …

绿源:“老大哥”冲刺IPO,新的故事如何讲?

又一家老牌电动两轮车企业“开”向了资本市场。 11月22日&#xff0c;绿源集团控股&#xff08;开曼&#xff09;有限公司&#xff08;以下简称“绿源集团”&#xff09;正式向港交所递交招股说明书&#xff0c;拟主板挂牌上市&#xff0c;中信建设国际担任独家保荐人&#xf…

ctfshow node.js专题

文章目录web334web335web336web337web338web339web340web341web342、web343web334 给了附件&#xff0c;然后进入后发现是一个登录框。 在附件中知道了账号密码&#xff0c;但是却无法登录。 先看user从哪里获取&#xff1a; var user findUser(req.body.username, req.bod…

虹科案例 | 订单自动分拣效率居然这么高?

Background 背景 过去&#xff0c;一家自动仓储和检索系统&#xff08;AS/RS&#xff09;梭子解决方案的制造商依靠车轮编码器来指示梭子沿轨道的位置。虽然这种解决方案已经使用了多年&#xff0c;也将继续使用&#xff0c;但它可能容易出现定位错误&#xff0c;这通常是由车…

[附源码]Python计算机毕业设计SSM考勤管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Kotlin高仿微信-第35篇-支付-二维码收款(二维码)

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

【python可视化】python编码规范、标准库与扩展库对象的导入与使用

&#x1f64b;‍ 哈喽大家好&#xff0c;本次是python数据分析、挖掘与可视化专栏第一期 ⭐本期内容&#xff1a;python编码规范、标准库与扩展库对象的导入与使用 &#x1f3c6;系列专栏&#xff1a;Python数据分析、挖掘与可视化 &#x1f44d;欢迎大佬指正&#xff0c;一起学…

嵌入式分享合集115

一、数字万用表电压、电流、电阻、电容、频率、电池、二极管等测量方法 数字万用表可用来测量直流和交流电压、直流和交流电流、电阻、电容、频率、电池、二极管等等。整机电路设计以大规模集成电路双积分A/D转换器为核心&#xff0c;并配以全过程过载保护电路&#xff0c;使之…

动态改变列数做分页

【问题】 My question is: How can I prepare template which will receive various number of columns in such way, that if they won’t fit to page, next columns would be printed on a second page. For example If I have 10 columns, but only 6 fit to the page 1,…

[附源码]计算机毕业设计springboot万佳商城管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

33K Star?这才是程序员需要的神器。。。

程序员宝藏库&#xff1a;https://gitee.com/sharetech_lee/CS-Books-Store 你想要的&#xff0c;这里都有&#xff01; 作为程序员用的比较多的工具是什么&#xff1f; 我觉得搜索引擎绝对能名列前茅。 在开发过程中&#xff0c;总会遇到这样或者那样的问题超出我们自身的知…

【愚公系列】华为云系列之ModelArts+AppCube带你识别101种西式美食【我的低代码AI体验】

文章目录前言1.ModelArts是什么2.AppCube是什么一、华为云系列之ModelArtsAppCube带你识别101种西式美食1. AI Gallery 订阅模型及部署2. 获取访问秘钥3. 使用示例安装包创建 AppCube 应用4. 创建 ModelArts 连接器前言 1.ModelArts是什么 ModelArts是面向AI开发者的一站式开…

[附源码]Python计算机毕业设计SSM考勤系统设计(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java项目:ssm教材征订与发放管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 教材征订与发放管理系统。本系统共分三种角色&#xff0c;分别为管理员、老师、班长&#xff1b;现在分别来介绍各个角色的功能&#xff1a; …