管道(匿名,有名)

news2024/12/29 14:38:05

文章目录

  • Linux 进程间通信的方式
  • 管道
  • 匿名管道
  • 有名管道

Linux 进程间通信的方式

在这里插入图片描述

管道

管道特点

  • 管道其实是一个在内核内存中维护的缓冲器,这个缓冲器的存储能力是有限的,不同的操作系统大小不一定相同
  • 管道拥有文件的特质:读操作、写操作
    • 匿名管道没有文件实体
    • 有名管道有文件实体,但不存储数据。可以按照操作文件的方式对管道进行操作
  • 一个管道是一个字节流,使用管道时不存在消息或者消息边界的概念,从管道读取数据的进程可以读取任意大小的数据块,而不管写入进程写入管道的数据块的大小是多少。
  • 通过管道传递的数据是顺序的,从管道中读取出来的字节的顺序和它们被写入管道的顺序是完全一样的
  • 在管道中的数据的传递方向是单向的,一端用于写入,一端用于读取,管道是半双工的。(半双工(Half Duplex)数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输。)
  • 从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据,在管道中无法使用 lseek() 来随机的访问数据
  • 匿名管道只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。【子进程fork之后与父进程共享文件描述符(✳理解)】

在这里插入图片描述

匿名管道

  • 管道也叫无名(匿名)管道,它是是 UNIX 系统 IPC(进程间通信)的最古老形式,所有的 UNIX 系统都支持这种通信机制。

  • example:统计一个目录中文件的数目命令:ls | wc –l,为了执行该命令,shell 创建了两个进程来分别执行 ls 和 wc。

在这里插入图片描述

匿名管道创建

  • int pipe(int pipefd[2])

    • 功能:创建一个匿名管道,用来进程间通信。
    • 参数:int pipefd[2] 这个数组是一个传出参数。
      pipefd[0] 对应的是管道的读端
      pipefd[1] 对应的是管道的写端
    • 返回值:成功 0,失败 -1

注意

  1. 管道默认是阻塞的:如果管道中没有数据,read阻塞,如果管道满了,write阻塞
  2. 匿名管道只能用于具有关系的进程之间的通信(父子进程,兄弟进程)

program1: 子进程发送数据给父进程,父进程读取到数据输出

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

int main(){
    int pipefd[2];
    int ret=pipe(pipefd);
    if(ret==-1){
        perror("pipe");
        exit(0);
    }
    pid_t pid=fork();
    //父进程
    if(pid>0){
        char buf[1024] = {0};
        int len = read(pipefd[0], buf, sizeof(buf));
        printf("parent recv : %s, pid : %d\n", buf, getpid());
    }else if(pid==0){
        char * str = "hello,i am child";
        write(pipefd[1], str, strlen(str));
    }
    return 0;
}

输出结果:

(base) user@ubuntu:~/Desktop/OS/NiuKe$ gcc -o test test.c 
(base) user@ubuntu:~/Desktop/OS/NiuKe$ ./test 
parent recv : hello,i am child, pid : 3552

program2: 父子进程相互通信

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//父子进程相互通信。
int main(){
    int pipefd[2];
    int ret=pipe(pipefd);
    if(ret==-1){
        perror("pipe");
        exit(0);
    }
    pid_t pid=fork();
    //父进程
    if(pid>0){
        printf("i am parent process, pid : %d\n", getpid());
        char buf[1024] = {0};
        while(1){
            //父进程接受子进程的数据,同时又给子进程发送数据。
            //从管道的读取端读取数据
            int len = read(pipefd[0], buf, sizeof(buf));
            printf("parent recv : %s, pid : %d\n", buf, getpid());
            // 向管道中写入数据
            char * str = "hello,i am parent";
            write(pipefd[1], str, strlen(str));
            sleep(1);
        }
    }else if(pid==0){
        printf("i am child process, pid : %d\n", getpid());
        char buf[1024] = {0};
        while(1){
            // 向管道中写入数据
            char * str = "hello,i am child";
            write(pipefd[1], str, strlen(str));
            sleep(1);

            int len = read(pipefd[0], buf, sizeof(buf));
            printf("child recv : %s, pid : %d\n", buf, getpid());
        }
    }
    return 0;
}

输出结果

(base) user@ubuntu:~/Desktop/OS/NiuKe$ gcc -o test test.c 
(base) user@ubuntu:~/Desktop/OS/NiuKe$ ./test 
i am parent process, pid : 3630
i am child process, pid : 3631
parent recv : hello,i am child, pid : 3630
child recv : hello,i am parent, pid : 3631
parent recv : hello,i am child, pid : 3630
child recv : hello,i am parent, pid : 3631
parent recv : hello,i am child, pid : 3630

【补充】:由于上面program2的代码加上了sleep,可以正常运行。如果去掉这句语句,则会导致进程自己往管道写同时又从管道读出的情况。因此,这个问题的解决办法就是提前关闭不需要的端口(读or写)。

总结

  • 读管道
    • 管道中有数据,read返回实际读到的字节数
    • 管道中无数据
      • 写端被全部关闭,read返回0(相当于读到文件的末尾)
      • 写端没有完全关闭,read阻塞等待
  • 写管道
    • 管道读端全部被关闭,进程异常终止(进程收到SIGPIPE信号)
    • 管道读端没有全部关闭:
      • 管道已满,write阻塞
      • 管道没有满,write将数据写入,并返回实际写入的字节数

有名管道

  • 匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道( FIFO),也叫命名管道、 FIFO文件。

  • 有名管道(FIFO)不同于匿名管道之处在于它提供了一个路径名与之关联,以 FIFO的文件形式存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与 FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO相互通信,因此,通过 FIFO不相关的进程也能交换数据

  • 一旦打开了 FIFO,就能在它上面使用与操作匿名管道和其他文件的系统调用一样的I/O系统调用了(如read()、write()和close())。与管道一样, FIFO也有一个写入端和读取端,并且从管道中读取数据的顺序与写入的顺序是一样的。 FIFO的名称也由此而来:先入先出

  • 有名管道(FIFO)匿名管道(pipe)有一些特点是相同的,不一样的地方在于

    • FIFO在文件系统中作为一个特殊文件存在,但 FIFO中的内容却存放在内存中
    • 当使用 FIFO的进程退出后, FIFO 文件将继续保存在文件系统中以便以后使用。
    • FIFO有名字,不相关的进程可以通过打开有名管道进行通信

有名管道创建

  1. mkfifo 文件名(shell命令创建)
  2. int mkfifo(const char *pathname, mode_t mode);
    • pathname:管道名称的路径
    • mode:文件的权限

program:两进程通过有名管道通信(单一发送)

写端:

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

// 向管道中写数据
int main() 
{
    // 1.判断文件是否存在
    int ret = access("test", F_OK);
    if(ret == -1) {
        printf("管道不存在,创建管道\n");
        
        // 2.创建管道文件
        ret = mkfifo("test", 0664);

        if(ret == -1) {
            perror("mkfifo");
            exit(0);
        }       

    }

    // 3.以只写的方式打开管道
    int fd = open("test", O_WRONLY);
    if(fd == -1) {
        perror("open");
        exit(0);
    }

    // 写数据
    for(int i = 0; i < 100; i++) {
        char buf[1024];
        sprintf(buf, "hello, %d\n", i);
        printf("write data : %s\n", buf);
        write(fd, buf, strlen(buf));
        sleep(1);
    }

    close(fd);

    return 0;
}

读端

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

// 从管道中读取数据
int main() 
{
    // 1.打开管道文件
    int fd = open("test", O_RDONLY);
    if(fd == -1) {
        perror("open");
        exit(0);
    }

    // 读数据
    while(1) {
        char buf[1024] = {0};
        // 这里不能写strlen(buf) 因为这里的含义是每次按固定长度读取,最开始strlen(buf)=0
        int len = read(fd, buf, sizeof(buf));
        if(len == 0) {
            printf("写端断开连接了...\n");
            break;
        }
        printf("recv buf : %s\n", buf);
    }

    close(fd);

    return 0;
}

运行

(不管哪端先执行)

在这里插入图片描述

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

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

相关文章

线扫相机DALSA-变行高拍照

CamExpert在线阵模式中默认的Buffer设置是Fixed Length。在这种设置下&#xff0c;在一帧采集结束前所接收到的新的帧触发信号都会被忽略。在有的应用中&#xff0c;需要新一帧的外触发信号能够中断当前帧的采集&#xff0c;开始新的一帧。这需要将Buffer设为Variable Length。…

【云原生】k8s之HPA,命名空间资源限制

内容预知 1.HPA的相关知识 2.HPA的部署运用 2.1 进行HPA的部署设置 2.2 HPA伸缩的测试演示 &#xff08;1&#xff09;创建一个用于测试的pod资源 (2)创建HPA控制器&#xff0c;进行资源的限制&#xff0c;伸缩管理 &#xff08;3&#xff09;进入其中一个pod容器仲&#xf…

Redhat OpenStack使用命令行发放云主机

OpenStack中各大组件的作用Glance&#xff1a;负责管理镜像&#xff08;镜像的上传、删除、下载&#xff09;Swift&#xff1a;提供镜像存储的空间Nova&#xff1a;负责配额的修改、启动云主机&#xff08;实例&#xff09;、创建密钥对、绑定弹性IP等Keystone&#xff1a;提供…

jQuery(二):属性、元素、尺寸位置操作、事件

jQuery属性操作内容文本值元素操作尺寸、位置操作事件注册事件处理事件对象拷贝对象属性操作 1.获取固有属性语法 prop(‘‘属性’’) 固有属性就是html自带的&#xff0c;例如a元素里面的 href &#xff0c;input 元素里面的 type。 2.设置固有属性语法 prop(‘‘属性’’, …

Python NumPy 搜索 数组

前言NumPy&#xff08;Numerical Python的缩写&#xff09;是一个开源的Python科学计算库。使用NumPy&#xff0c;就可以很自然地使用数组和矩阵。NumPy包含很多实用的数学函数&#xff0c;涵盖线性代数运算、傅里叶变换和随机数生成等功能。本文主要介绍Python NumPy 搜索 数组…

Linux小黑板(8)管道

"让我们,笑吧"一、什么是通信?管道是属于进程间通信的一个实现方式。再讲管道之前呢&#xff0c;我们先来说说什么叫做进程间通信。我们日常生活中&#xff0c;给自己的家人、朋友给一个call&#xff0c;或者弹一条微信、QQ等等&#xff0c;从而让人家能够知道我们想…

Databend 开源周报第 76 期

英文版移步&#xff1a;https://databend.rs/blog/2023-01-11-databend-weekly Databend 是一款强大的云数仓。专为弹性和高效设计。自由且开源。即刻体验云服务&#xff1a;https://app.databend.com 。 What’s New 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的…

vue 中由浅拷贝引发问题的一些场景

在工作的过程中踩了很多的由浅拷贝导致的坑&#xff0c;今天总结在这里&#xff0c;希望对大家有所帮助 1. 组件中直接抛出一个引用类型变量 &#x1f330;举个例子 &#xff08;ps: 以下代码为伪代码&#xff0c;主要展示逻辑用&#xff09; 子组件&#xff08;uploadImg&a…

线 程 同 步、线程的死锁问题

线程同步&#xff1a; 模拟售票程序出现问题&#xff1a;当多个线程同时访问共享数据时&#xff0c;产生无序、重复、超额售票等多线程安全问题 解决&#xff1a;将多个线程需要访问的共享数据&#xff0c;包装起来视为一个整体&#xff0c;确保一次只有一个线程执行流访问共享…

春节福利丨神策数据 2022 年数字化营销资料打包全送

2022 年&#xff0c;神策数据出品多份行业研究报告&#xff0c;覆盖银行、证券、零售、教育、电商、融合媒体等多个行业&#xff0c;帮助更多企业通过多视角洞见紧握数字化营销的方向和趋势&#xff0c;用方法论结合落地实践驱动企业数字化经营。01B2B 电商数字化运营聚焦四类 …

【自学Python】Python查找字符串位置

Python查找字符串位置 大纲 Python查找字符串位置教程 在开发过程中&#xff0c;很多时候我们有在一个 字符串 中查找另一个字符串位置的需求&#xff0c;在 Python 中&#xff0c;在一个字符串中查找另一个字符串的位置我们使用 index() 函数。 index() 函数的功能与 find(…

力扣(78.90)补9.22

78.子集 感觉不太难&#xff0c;但是就是不会写。感觉回溯里有很多细节问题。 class Solution { private: vector<vector<int>> res; vector<int> num; void back(vector<int>& nums,int index,int end){ res.push_back(num); …

【JavaEE初阶】第三节.多线程基础篇

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 文章目录 前言 一、认识线程 二、多线程程序 2.1 第一个Java多线程程序 2.2 怎么样观察线程的详细情况 2.3 sleep方法 2.4 run 和 start 方法的区别是什么 三、创…

在springboot中配置热部署

今天什么节日也不是&#xff0c;那就祝大家今天快乐。 热部署 所谓热部署&#xff0c;就是在应用正在运行的时候升级软件&#xff0c;却不需要重新启动应用。对于Java应用程序来说&#xff0c;热部署就是在运行时更新Java类文件。在以往&#xff0c;我们对java代码进行修改之后…

【HBU】大一下期末重点

物理简答题一、牛顿第二定律&#xff08;Fdp/dt &#xff09;&#xff1a;1. 飞机怕小鸟:Fdp/dt 力与作用时间的乘积等于物体动量的变化。由于小鸟与飞机相对速度很大&#xff0c;作用时间很短,产生的作用力很大&#xff0c;当小鸟与飞机向撞&#xff0c;效果与同质量炮弹撞飞机…

正则表达式入门及常用正则表达式

常用正则表达式 1 正则表达式的基础概念 1.1 预定义字符 . 表示任何字符&#xff08;与行结束符可能匹配也可能不匹配&#xff09; \d 数字&#xff1a;[0-9] \D 非数字字符&#xff1a;[^0-9] \s 空白字符&#xff1a;[\t\n\xoB\f\r] \S 非空白字符&#xff1a;[^\s] \w 单…

k8s的YAML部署rocketmq记录

说明 测试环境是k8s集群&#xff0c;在上边部署一套单节点的rocketmq nameserver部署 Service和StatefulSet脚本如下 apiVersion: v1 kind: Service metadata:labels:app: rocketmqnamesrvname: rocketmqnamesrv spec:type: ClusterIPports:- port: 9876targetPort: 9876na…

Pytorch深度学习【十四】

批量归一化 归一化 损失出现在最后&#xff0c;后面的层(高级语义层)训练较快数据输入在最底部 底部的层训练慢底部层一变化&#xff0c;所有高级语义层都得跟着变最后的那些层需要重新学习多次—收敛速度变慢 问题—是否可以在学习底部层的时候避免变化顶部层 批量归一化 固定…

8 个精彩的免费 G​​IS 软件资源分享

有人说&#xff1a;一个人从1岁活到80岁很平凡&#xff0c;但如果从80岁倒着活&#xff0c;那么一半以上的人都可能不凡。 生活没有捷径&#xff0c;我们踩过的坑都成为了生活的经验&#xff0c;这些经验越早知道&#xff0c;你要走的弯路就会越少。 GIS 软件有两种通用格式&a…

【面试题】2023年前端最新面试题-性能优化篇

原文见&#xff1a;语雀&#xff08;https://www.yuque.com/deepstates/interview/xtt59x&#xff09; ● 性能指标 ● 分析工具 ● 优化方式 ○ 加载 ○ 渲染 ● 专题优化 ○ 技术栈&#xff1a;react ○ 浏览器 ○ 打包工具&#xff1a;webpack ● 项目 ⭐️⭐️⭐️ 相关知…