【无名管道】无名管道(Pipe)与进程通信:快速上手

news2024/11/18 17:41:25

目录

0. 管道概述:

1. 管道特点

2. 管道创建:pipe函数

3. 管道的读写特点

4. 通过fcntl函数设置文件的阻塞特性

5. 查看管道缓冲区命令

总结:


0. 管道概述:

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

        无名管道是创建在内核空间的,多个进程知道同一个无名管道的空间,就可以利用它来进行通信。(在32位操作系统下,任何一个进程创建时,系统都会给其分配4G的虚拟内存,3G为用户空间,1G为内核空间)

        无名管道虽然是在内核空间创建的,但是会给当前用户进程两个文件描述符,一个负责执行读操作,一个负责写操作。

1. 管道特点

1) 半双工,数据在同一时刻只能在一个方向上流动。

2) 数据只能从管道的一端写入,从另一端读出。

3) 写入管道中的数据遵循先入先出的规则。

4) 管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

5) 管道不是普通的文件,不属于某个文件系统,其只存在于内存中。

6) 管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

7) 从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。

8) 管道没有名字,只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。

        对于管道特点的理解,我们可以类比现实生活中管子,管子的一端塞东西,管子的另一端取东西。管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符。

2. 管道创建:pipe函数

pipe函数:

#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建无名管道,返回两个文件描述符负责对管道进行读写操作
参数:
    pipefd : 为 int 型数组的首地址,其存放了管道的文件描述符 pipefd[0]、pipefd[1]。

                 pipefd[0]:负责对管道执行读操作。

                 pipefd[1]:负责对管道执行写操作。
                 一般文件 I/O的函数都可以用来操作管道(lseek() 除外)。
返回值:
    成功:0
    失败:-1

 代码示例:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
    int fd_pipe[2];
    if (pipe(fd_pipe) == -1) {
        perror("fail to pipe");
        exit(1);
    }

    printf("fd_pipe[0] = %d\n", fd_pipe[0]);
    printf("fd_pipe[1] = %d\n", fd_pipe[1]);

    if (write(fd_pipe[1], "hello world ", 12) == -1) {
        perror("fail to write");
        exit(1);
    }
    write(fd_pipe[1], "nihao beijing", strlen("nihao biejing") + 1);
    char buf[32] = "";
    ssize_t bytes;
    if ((bytes = read(fd_pipe[0], buf, sizeof(buf))) == -1) {
        perror("fail to read");
        exit(1);
    }
    printf("buf = %s\n", buf);
    printf("bytes = %ld\n", bytes);
    return 0;
}

 父子进程通过管道实现数据的传输

 示例代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
    {
        perror("fail to pipe");
        exit(1);
    }
    int pipeff[2];
    if (pipe(pipeff) == -1)
    {
        perror("fail to pipe");
        exit(1);
    }

    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if (pid > 0)
    {
        char buf[128] = "";
        char buu[128] = "";
        while (1)
        {
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1] = '\0';

            if (write(pipefd[1], buf, sizeof(buf)) == -1)
            {
                perror("fail to write");
                exit(1);
            }

            if (read(pipeff[0], buu, sizeof(buu)) == -1)
            {

                perror("fail to read ");
                exit(1);
            }
            printf("buu = %s\n", buu);
        }

    }
    else
    {
        char buf[128] = "";
        char buu[128] = "";
        while (1)
        {
            if (read(pipefd[0], buf, sizeof(buf)) == -1)
            {

                perror("fail to read ");
                exit(1);
            }
            printf("buf = %s\n", buf);


            fgets(buu, sizeof(buu), stdin);
            buu[strlen(buu) - 1] = '\0';

            if (write(pipeff[1], buu, sizeof(buu)) == -1)
            {
                perror("fail to write");
                exit(1);
            }
        }

    }
    return 0;
}

3. 管道的读写特点

使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):

1. 读写端都存在,只读不写:

    //读写端都存在,只读不写
    //如果管道中有数据,会正常读取数据
    //如果管道中没有数据,则读操作会阻塞等待,直到有数据为止

2. 读写端都存在,只写不读

    //读写端都存在,只写不读
    //如果一直执行写操作,则无名管道对应的缓冲区会被写满,写满之后,write函数也会阻塞等待
    //默认无名管道的缓冲区64K字节

3. 只有读端

    //关闭文件描述符,只有读端
    //如果管道中有数据,则读操作正常读取数据
    //如果管道中没有数据,则read函数返回0

4. 只有写段

    //关闭读端,只有写端
    //关闭读端,一旦执行写操作,立刻产生一个SIGPIPE(管道破裂)
    //这个信号默认处理方式是退出进程

读写端都存在,只读不写

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
    {
        perror("fail to pipe");
        exit(1);
    }
    //读写端都存在,只读不写
    //如果管道中有数据,会正常读取数据
    //如果管道中没有数据,则读操作会阻塞等待,直到有数据为止
    write(pipefd[1], "hello world", 11);
    char buf[128] = "";
    if (read(pipefd[0], buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("buf = %s\n", buf);

    if (read(pipefd[0], buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("buf = %s\n", buf);
    return 0;
}
~

读写端都存在,只写不读

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
    {
        perror("fail to pipe");
        exit(1);
    }
    //读写端都存在,只写不读
    //如果一直执行写操作,则无名管道对应的缓冲区会被写满,写满之后,write函数也会阻塞等待
    //默认无名管道的缓冲区64K字节
    int num = 0;
    while (1)
    {
        if (write(pipefd[1], "666", 1024) == -1)
        {
            perror("fail to write");
            exit(1);
        }
        num++;
        printf("num = %d\n", num);
    }
    return 0;
}

只有读端

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>


int main()
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
    {
        perror("fail to pipe");
        exit(1);
    }
    write(pipefd[1], "hello world", 11);
    //关闭文件描述符,只有读端
    //如果管道中有数据,则读操作正常读取数据
    //如果管道中没有数据,则read函数返回0
    close(pipefd[1]);

    char buf[128] = "";
    ssize_t bytes;
    if ((bytes = read(pipefd[0], buf, sizeof(buf))) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("bytes = %ld\n", bytes);
    printf("buf = %s\n", buf);

    memset(buf, 0, sizeof(buf));
    if ((bytes = read(pipefd[0], buf, sizeof(buf))) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("bytes = %ld\n", bytes);
    printf("buf = %s\n", buf);
    return 0;
}

只有写

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>

void handler(int sig) {
    printf("SIGPIPE发生了,管道破裂\n");
}
int main()
{
    signal(SIGPIPE, handler);
    int pipefd[2];
    if (pipe(pipefd) == -1)
    {
        perror("fail to pipe");
        exit(1);
    }
    //关闭读端,只有写端
    //关闭读端,一旦执行写操作,立刻产生一个SIGPIPE(管道破裂)
    //这个信号默认处理方式是退出进程
    close(pipefd[0]);

    int num = 0;
    while (1)
    {
        if (write(pipefd[1], "hello", 1024) == -1)
        {
            perror("fail to write");
            exit(1);
        }
        num++;
        printf("num = %d\n", num);
    }
    return 0;
}

4. 通过fcntl函数设置文件的阻塞特性

设置为阻塞:

        fcntl(fd,F_SETFL,0);

设置为非阻塞:

        fcntl(fd,F_SETFL,O_NONBLOCK);

非阻塞:如果是阻塞,管道中没有数据,read会一直等待,直到有数据才会继续运行,否则一直等待。

设置方法:

//获取原来的flags
int flags = fcntl(fd[0], F_GETFL);
// 设置新的flags
flag |= O_NONBLOCK;
// flags = flags | O_NONBLOCK;
fcntl(fd[0], F_SETFL, flags);

结论: 如果写端没有关闭,读端设置为非阻塞, 如果没有数据,直接返回-1。

        如果是非阻塞,read函数运行时,会先看一下管道中是否有数据,如果有数据,则正常运行读取数据,如果管道中没有数据,则read函数会立即返回,继续下面代码运行。

include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>

int main()
{
    int fd_pipe[2];
    char buf[] = "hello world";
    pid_t pid;

    if (pipe(fd_pipe) == -1)
    {
        perror("fail to pipe");
        exit(1);
    }

    pid = fork();
    if (pid < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if (pid > 0)
    {
        fcntl(fd_pipe[0], F_SETFL, O_NONBLOCK);
        while (1)
        {
            memset(buf, 0, sizeof(buf));
            read(fd_pipe[0], buf, sizeof(buf));
            printf("buf = %s\n", buf);
            sleep(1);
        }
    }
    else
    {
        while (1)
        {
            sleep(5);
            write(fd_pipe[1], buf, strlen(buf));
        }
    }
    return 0;
}

5. 查看管道缓冲区命令

可以使用ulimit -a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。

 查看管道缓冲区函数

#include <unistd.h>
​
long fpathconf(int fd, int name);
功能:该函数可以通过name参数查看不同的属性值
参数:
    fd:文件描述符
    name:
        _PC_PIPE_BUF,查看管道缓冲区大小
        _PC_NAME_MAX,文件名字字节数的上限
返回值:
    成功:根据name返回的值的意义也不同。
    失败: -1

示例:

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe error");
        exit(1);
    }
​
    long num = fpathconf(fd[0], _PC_PIPE_BUF);
    printf("num = %ld\n", num);
​
    return 0;
}

总结:

        无名管道是一种强大且基础的进程间通信机制,它使得具有亲缘关系的进程之间能够方便地共享数据。无名管道的使用,无论是在简单的Shell命令,还是在复杂的多进程应用中,都有着广泛的应用。

        注意:无名管道只能在具有公共祖先的进程之间使用,且只能实现单向通信。

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

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

相关文章

019、数据库管理之备份恢复管理(BR)

备份的重要性 数据库恢复审计和分析典型DBA任务 备份的类型 热备&#xff0c;允许应用程序完全访问数据。冷备&#xff0c;不允许应用程序访问年数据温备&#xff0c;允许应用程序读取&#xff0c;但不能修改 热备份 热备份是在读取和修改数据时进行的&#xff0c;几乎不会…

怎么高效批量调整视频的尺寸大小

大家平时在剪辑视频的过程中&#xff0c;是否会遇到视频尺寸大小不合适的情况&#xff0c;当我们遇到这种情况时&#xff0c;如何才能快速批量地解决呢&#xff1f;有没有什么方法可以快速批量修改视频尺寸大小呢&#xff1f;跟着小编一起来看看我平时批量修改视频尺寸的方法。…

联邦学习的架构思想

目录 联邦学习介绍&#xff08;非常详细&#xff09; 联邦学习的由来 联邦学习的发展历程 1) 机器学习 2) 分布式机器学习 3) 隐私保护技术 4) 联邦学习 联邦学习的规范与标准 联邦学习的架构思想 联邦学习的社区与生态 联邦学习介绍&#xff08;非常详细&#xff09…

Lenovo联想笔记本电脑 小新 Air-14 2020 Intel平台IIL版(81YJ)原装Win10系统恢复原厂OEM预装专用系统

Lenovo联想笔记本电脑&#xff0c;小新 Air-14 2020 Intel平台IIL版(81YJ)出厂Windows10系统原装系统镜像&#xff0c;恢复原厂状态 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1…

基于微信小程序的上课签到系统(数据库+报告+文档+演示视频)

系统技术语言介绍&#xff1a; 本系统采用了SSM (Spring Spring MVC Mybatis&#xff09;架构&#xff0c; MySQL作为基础数据库&#xff0c;微信开发工具作为前端基础&#xff0c;前端采用了 wxml的设计语言&#xff0c;Idea作为后台的开发工具。 功能列表&#xff1a; 1.…

从数字图像到音视频学习:我的学习之旅

数字图像是一门广泛应用于计算机视觉、图像处理和计算机图形学等领域的学科&#xff0c;而音视频学习则涵盖了音频和视频的处理、分析和应用。 如果你最开始接触数字图像&#xff0c;可能会学习一些基本概念&#xff0c;例如像素、分辨率、色彩空间和图像处理算法等。这可能涉…

java项目之药源购物网站ssm源码+文档

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的药源购物网站。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&#xff…

隔离变压器和浪涌保护器SPD有哪些不同作用

浪涌保护装置&#xff08;SPD&#xff09;和隔离变压器的作用经常混淆。为了保护敏感的电子设备和设备免受瞬态事件的影响&#xff0c;必须考虑各种因素。 无论瞬态来自外部还是内部&#xff0c;信号噪声、共模瞬变、差模瞬变和接地电位上升都可能导致保护不力的设备出现问题。…

vue中使用mock.js

安装mock npm install mockjs --save-dev或者在ui中选择依赖 查看安装是否成功 进入package.json文件 配置mock 在src目录下新建mock文件夹&#xff0c;在mock文件夹下建立index.js 配置模拟请求数据 index.js import Mock from mockjsconst loginData Mock.mock(http://l…

【 云原生 kubernetes 】- 单点登录Authelia + OpenLdap

文章目录 简介AutheliaOpenLdap属性介绍 Helm部署openldap配置调整数据持久化部署使用命令行Web Authelia 页面访问反向代理进行集成流程 ⚡️: OpenLDAP是轻量级目录访问协议&#xff08;LDAP&#xff09;的开源实现&#xff0c;它提供了一种存储和访问关于用户、组、计算机和…

使用Python将图片转pdf

使用Python将图片转pdf 1. 效果图2. 源码参考 今天需要把图片转pdf&#xff0c;发现迅捷pdf转换开始默认带水印了&#xff0c;收费版那算了&#x1f602;&#x1f602;&#xff0c;那就用Python吧&#xff0c;可单转&#xff0c;可批量转。 参考: https://blog.csdn.net/qq_53…

我是如何在linux下 安装 mongoDB的

背景 接到上级任务&#xff0c;需要在测试环境搭建一套mongodb&#xff1b;对于从来没接触过mongodb的小白&#xff0c;但拥有多年编程经验的我来说&#xff0c;不虚好吧。因为我有自己做事的思考。 首先向领导确认两点&#xff1a; 搭建mongo db是单机版的&#xff0c;还是主…

自定义Veiw实战《渐变的文字》

自定义Veiw实战《渐变的文字》 前言SpannableString什么时候使用常用场景 自定义TextView控件什么时候使用实战歌词渐变的文字先看效果第一步&#xff1a;初始化第二步&#xff1a;测量文字&#xff0c;并确认文字绘制的起点第三步&#xff1a;绘制文字第四步&#xff1a;调用 …

Node.js安装及npm国内镜像配置

node.js中文网站下载&#xff0c;以16.17为例&#xff01;&#xff01;&#xff01;&#xff01; 安装 1.网站链接 CNPM Binaries Mirror (npmmirror.com) 根据自己的版本选择&#xff0c;我是64位选择了zip版 2.下载完成后解压 我选择的目录是&#xff08;E:\Environments…

mac中在html中引入react.development.js、react-dom.development.js报错

错误描述 Uncaught ReferenceError: process is not definedUncaught Invariant Violation: ReactDOM was loaded before React. Make sure you load the React package before loading ReactDOM.Inline Babel script:3 Uncaught ReferenceError: React is not defined解决 1…

react中数组列表的简单新增,删除以及修改实践

react中数组列表的简单新增&#xff0c;删除以及修改实践 效果示例图示例代码 index.jsx示例样式 index.scss 效果示例图 新增图片 编辑图片 示例代码 index.jsx import { useEffect, useState } from "react"; import "./index.scss";// 创建数据 fu…

WebGIS 信息系统-数据库设计

WebGIS 信息系统-数据库设计 E-R 图设计创建数据库及数据表开发环境及环境配置1 .服务器端2. 客户端1 &#xff09;配置Struts22 &#xff09;配置Hibernate3 &#xff09;配置Spring4 &#xff09;配置Web . xml 基于GIS 的交通信息系统采用PostgreSQL 为属性数据库&#xff0…

虚拟机中Ubuntu 22上传框被黑框包裹的解决方法

虚拟机中Ubuntu 22上传框被黑框包裹的解决方法 现象解决方法 现象 在vm17下的ubuntu22使用上传表单时出现了这种不和谐的现象&#xff0c;被领导批评一通。最后费劲心思&#xff0c;找到了这个问题的解决方法。 解决方法 解决方法特别容易&#xff0c;在虚拟机的设置中&…

TypeScript 【type】关键字的进阶使用方式

导语&#xff1a; 在前面章节中&#xff0c;我们了解到 TS 中 type 这个关键字&#xff0c;常常被用作于&#xff0c;定义 类型别名&#xff0c;用来简化或复用复杂联合类型的时候使用。同时也了解到 为对象定义约束接口类型 的时候所使用的是 Interfaces。 其实对于前面&#…

ansible - 安装 -【kodcloud教程】

目录 目录结构、主机清单 【1】安装基础环境 【2】webserver - 安装nginx 【3】安装php环境 【4】lb-server 【5】redis安装 【6】 kodcloud 【7】wordpress-proxy ansible10.0.0.10lb-server10.0.0.11web0110.0.0.15web0210.0.0.16redis10.0.0.20 目录结构、主机清单…