107.管道(有名、无名管道)

目录

管道

无名管道 

无名管道的创建

无名管道的基本用法

有名管道 

写管道的进程:

读管道的进程:


管道

        管道是一种进程间通信的机制,允许一个进程的输出直接作为另一个进程的输入。在Linux 系统中,管道通过 pipe 系统调用来创建。一个典型的管道由两个文件描述符表示,分别用于读取和写入。

因为管道数据是通过队列来维护的,我们先来分析一个管道中数据的特点:

  • 管道对应的内核缓冲区大小是固定的,默认为4k(也就是队列最大能存储4k数据)
  • 管道分为两部分:读端和写端(队列的两端),数据从写端进入管道,从读端流出管道。
  • 管道中的数据只能读一次,做一次读操作之后数据也就没有了(读数据相当于出队列)。
  • 管道是单工的:数据只能单向流动, 数据从写端流向读端。
  • 对管道的操作(读、写)默认是阻塞的
  • 读管道:管道中没有数据,读操作被阻塞,当管道中有数据之后阻塞才能解除
  • 写管道:管道被写满了,写数据的操作被阻塞,当管道变为不满的状态,写阻塞解除。
// 读管道
ssize_t read(int fd, void *buf, size_t count);
// 写管道的函数
ssize_t write(int fd, const void *buf, size_t count);

无名管道 

        无名管道是一种最简单的管道形式,通过 pipe 系统调用创建。它是单向的,只能用于具有亲缘关系的进程之间的通信,通常在创建子进程前调用 pipe,然后通过 fork 创建子进程,实现父子进程之间的通信。

无名管道的创建

#include <unistd.h>

int pipe(int pipefd[2]);//pipefd 是一个长度为 2 的整型数组,用于存储两个文件描述符
/*pipefd()成功返回 0,失败返回-1
  fds[0]是管道读端的描述符
  fds[1]是管道写端的描述符 */

无名管道的基本用法

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

int main()
{
    int pipefd[2];

    // 创建管道
    if (pipe(pipefd) == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    // 创建子进程
    pid_t pid = fork();

    if (pid == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid > 0)
    {
        // 父进程
        close(pipefd[0]); // 关闭管道读取端

        const char *message = "Hello from parent!";
        write(pipefd[1], message, strlen(message));

        close(pipefd[1]); // 关闭管道写入端
    }
    else if (pid == 0)
    {
        // 子进程
        close(pipefd[1]); // 关闭管道写入端

        char buffer[50];
        ssize_t bytesRead = read(pipefd[0], buffer, sizeof(buffer));
        if (bytesRead == -1)
        {
            perror("read");
            exit(EXIT_FAILURE);
        }

        buffer[bytesRead] = '\0';
        printf("Child received: %s\n", buffer);

        close(pipefd[0]); // 关闭管道读取端
    }

    return 0;
}
  1. 创建管道: 通过 pipe(pipefd) 创建了一个无名管道,pipefd[0] 是读取端,pipefd[1] 是写入端。

  2. 创建子进程: 通过 fork() 创建了一个子进程。父进程和子进程都会执行后续的代码。

  3. 父进程写入消息: 在父进程中,关闭了读取端 pipefd[0],然后使用 write(pipefd[1], message, strlen(message)) 向管道的写入端 pipefd[1] 写入消息 "Hello from parent!"。

  4. 子进程读取消息: 在子进程中,关闭了写入端 pipefd[1],然后使用 read(pipefd[0], buffer, sizeof(buffer)) 从管道的读取端 pipefd[0] 读取消息。读取的内容被存储在 buffer 中,并通过 printf 打印出来。

  5. 关闭文件描述符: 父子进程分别关闭了管道中不再需要的文件描述符。

这个例子中,父进程向子进程发送了一条消息,子进程接收到并打印了这条消息。需要注意的是,管道是一种半双工通信机制,数据是单向流动的。在实际应用中,可能需要创建两个管道来实现双向通信。此外,为了防止产生僵尸进程,通常在父进程中使用 wait 等待子进程的结束。

有名管道 

        有名管道拥有管道的所有特性,之所以称之为有名是因为管道在磁盘上有实体文件, 文件类型为p ,有名管道文件大小永远为0,因为有名管道也是将数据存储到内存的缓冲区中,打开这个磁盘上的管道文件就可以得到操作有名管道的文件描述符,通过文件描述符读写管道存储在内核中的数据。

        有名管道也可以称为 fifo (first in first out),使用有名管道既可以进行有血缘关系的进程间通信,也可以进行没有血缘关系的进程间通信。创建有名管道的方式有两种,一种是通过命令,一种是通过函数。

通过函数

#include <sys/types.h>
#include <sys/stat.h>
// int open(const char *pathname, int flags, mode_t mode);
int mkfifo(const char *pathname, mode_t mode);

参数:

  • pathname: 要创建的有名管道的名字
  • mode: 文件的操作权限, 和open()的第三个参数一个作用,最终权限: (mode & ~umask)
  • 返回值:创建成功返回 0,失败返回 -1

        不管是有血缘关系还是没有血缘关系,使用有名管道实现进程间通信的方式是相同的,就是在两个进程中分别以读、写的方式打开磁盘上的管道文件,得到用于读管道、写管道的文件描述符,就可以调用对应的read()、write()函数进行读写操作了。

写管道的进程:

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

int main()
{
    int fd = open("FIFO", O_WRONLY);// 打开有名管道 "FIFO",使用写入权限
    assert(fd != -1); // 检查文件打开是否成功

    printf("open FIFO success\n");

    // 进入一个无限循环,提示用户输入数据
    while (1)
    {
        printf("please input: ");// 提示用户输入
        char buff[128] = {0};

        fgets(buff, 128, stdin); 从标准输入中读取用户输入的数据,存储在 buff 缓冲区中

        write(fd, buff, strlen(buff) - 1);// 将用户输入的数据写入到已打开的有名管道文件描述符 fd 中

        // 检查用户输入是否为 "end",如果是,退出循环
        if (strncmp(buff, "end", 3) == 0)
        {
            break;
        }
    }
    // 关闭有名管道文件描述符
    close(fd);
    exit(0);
}

这是一个用于向有名管道写入数据的程序。让我为您解释一下:

  1. open("FIFO", O_WRONLY) 打开名为 "FIFO" 的有名管道,使用写入权限。如果该文件不存在,会创建它。

  2. 进入一个无限循环,提示用户输入数据:

    • fgets(buff, 128, stdin) 从标准输入中读取用户输入的数据,存储在 buff 缓冲区中。这里假设用户输入的数据不超过 128 个字符。

    • write(fd, buff, strlen(buff) - 1) 将用户输入的数据写入到已打开的有名管道文件描述符 fd 中。这里使用 strlen(buff) - 1 是为了去掉输入中的换行符。

    • 检查用户输入是否为 "end",如果是,退出循环。

  3. 关闭有名管道文件描述符 (close(fd)),退出程序。

读管道的进程:

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

int main() {
    // 打开有名管道 "FIFO",使用只读权限
    int fd = open("FIFO", O_RDONLY);
    assert(fd != -1); // 检查文件打开是否成功

    printf("open FIFO success\n");

    // 进入一个无限循环,读取有名管道中的数据并输出
    while (1) {
        char buff[128] = {0};
        // 从有名管道中读取数据,存储在 buff 缓冲区中
        int n = read(fd, buff, 127);

        // 如果读取到的数据小于等于 0,或者读取到的数据为 "end",则退出循环
        if (n <= 0 || 0 == strncmp(buff, "end", 3)) {
            break;
        }

        // 输出读取到的数据
        printf("%s\n", buff);
    }

    // 关闭有名管道文件描述符
    close(fd);
    exit(0);
}

这段代码是一个使用有名管道(FIFO)进行进程间通信的示例。有名管道是一种特殊的文件,可用于在不相关的进程之间传递数据。

  1. 打开有名管道文件 "FIFO",并使用只读权限打开,open("FIFO", O_RDONLY)
  2. 检查文件打开是否成功,如果失败则退出程序。
  3. 进入一个无限循环,用于读取有名管道中的数据并输出。
  4. 使用 read 从有名管道中读取数据,将其存储在 buff 缓冲区中,最多读取 127 个字节。
  5. 如果读取到的数据小于等于 0,或者读取到的数据为 "end",则退出循环。
  6. 输出读取到的数据。
  7. 关闭有名管道文件描述符。
  8. 退出程序。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

javaSwing酒店管理系统

一、 使用方法&#xff1a; 在使用前&#xff0c;需要到druid.properties 配置文件中&#xff0c;修改自己对应于自己数据库的属性&#xff1b;如用户名&#xff0c;密码等 driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql:///hotel?useUnicodetrue&characterEn…

【C++ 程序设计入门基础】- 第3节-循环结构02

目录 while 语句 案例 while 循环 输入一个整数 n &#xff0c;输出 1~n 的所有整数。 查看运行结果&#xff1a; while 语句结构解析 do while 语句 案例 do while 循环 输入一个整数n&#xff0c;输出1&#xff5e;n的所有整数。 查看运行结果 while、do while的区别 …

Go压测工具

前言 在做Go的性能分析调研的时候也使用到了一些压测方面的工具&#xff0c;go本身也给我们提供了BenchMark性能测试用例&#xff0c;可以很好的去测试我们的单个程序性能&#xff0c;比如测试某个函数&#xff0c;另外还有第三方包go-wrk也可以帮助我们做http接口的性能压测&…

TCP为什么可靠之“重传机制”

TCP重传机制 TCP针对数据包丢失的情况&#xff0c;会通过重传机制解决&#xff0c;包括像超时重传、快速重传、选择确认SACK、D-SACK 超时重传 TCP会设置一个定时器&#xff0c;如果在发送数据之后的规定时间内&#xff0c;没有收到对方的ACK报文&#xff0c;就会触发重新发…

【Qt开发流程】之容器类2:使用STL风格迭代器进行遍历

概述 对于每个容器类&#xff0c;都有两种stl风格的迭代器类型:一种提供只读访问&#xff0c;另一种提供读写访问。应该尽可能使用只读迭代器&#xff0c;因为它们比读写迭代器快。 STL迭代器的API以数组中的指针为模型。例如&#xff0c;操作符将迭代器推进到下一项&#xf…

vue2+datav可视化数据大屏(1)

开始 &#x1f4d3; 最近打算出一个前端可视化数据大屏的系列专栏&#xff0c;这次将很全面的教大家设计可视化大屏&#xff0c;从开始到打包结束&#xff0c;其中&#xff0c;包括如何设计框架&#xff0c;如何封装axios&#xff0c;等等&#xff0c;本次使用的数据均为mock数…

关于AllowBeanDefinitionOverriding属性设置问题

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是「奇点」&#xff0c;江湖人称 singularity。刚工作几年&#xff0c;想和大家一同进步&#x1f91d;&#x1f91d; 一位上进心十足的【Java ToB端大厂…

二叉树的OJ练习(二)

通过前序遍历数组构建二叉树 题目&#xff1a;通过前序遍历的数组&#xff08;ABD##E#H##CF##G##&#xff09;构建二叉树 TreeNode* TreeCreat(char* a,int* pi) {if(a[*pi] #){(*pi);return NULL; }TreeNode* root (TreeNode*)malloc(sizeof(TreeNode));if(root NULL){p…

【Unity动画】Avatar Mask

创建 Avatar Mask可以设置那一部分骨骼运动和不运动 然后放在状态机里面的层中来混合 【后续完善】

前端入门:HTML初级指南,网页的简单实现!

代码部分&#xff1a; <!DOCTYPE html> <!-- 上方为DOCTYPE声明&#xff0c;指定文档类型为HTML --> <html lang"en"> <!-- html标签为整个页面的根元素 --> <head> <!-- title标签用于定义文档标题 --> <title>初始HT…

快速测试 3节点的redis sentinel集群宕机2个节点以后是否仍能正常使用

有同事问我&#xff0c;三个redis sentinel节点&#xff0c;宕机两个节点以后&#xff0c;是否还能够正常的通过redis sentinel正常访问redis的数据。我想了想&#xff0c;理论上是可以的&#xff0c;但是我没试过&#xff0c;今天有时间就测试了一下。搭建环境和测试代码的过程…

Linux Component概述和高通component的使用

1 Linux为什么要引入Component框架&#xff1f; 为了让subsystem按照一定顺序初始化设备才提出来的。 subsystem中由很多设备模块&#xff0c;内核加载这些模块的时间不确定。子系统内有些模块是需要依赖其它模块先初始化才能进行自己初始化工作(例如v4l2 subdev和v4l2 video …

虚拟化之通用计时器

Arm架构包含通用定时器(Generic Timer),这是每个处理器中都有的一组标准化定时器。通用定时器包括一组比较器,这些比较器与一个共同的系统计数进行比较。当比较器的值等于或小于系统计数时,该比较器会生成中断。在下图中,我们可以看到系统中的通用定时器(橙色)以及其比…

HTML+CSS+JavaScript制作简单轮播图

一 运行效果 二 图片资源 三 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>img{position:absolute;top:6%;left:20%;width:800px;height:240px;}.picture {back…

【FMCW毫米波雷达设计 】 — FMCW波形

原书&#xff1a;FMCW Radar Design 1 引言 本章研究驱动FMCW雷达的主要波形:线性调频(LFM)波形。我们研究信号的行为及其性质。随后&#xff0c;本章讨论了匹配滤波理论&#xff0c;并研究了压缩这种波形的技术&#xff0c;特别是所谓的拉伸处理&#xff0c;它赋予FMCW雷达极…

最长子序列问题(LCS)--动态规划解法

题目描述&#xff1a; 如果Z既是X的子序列&#xff0c;又是Y的子序列&#xff0c;则称Z为X和Y的公共子序列。 如果给定X、Y&#xff0c;求出最长Z及其长度。 示例&#xff1a; 输入 ABCPDSFJGODIHJOFDIUSHGD OSDIHGKODGHBLKSJBHKAGHI 输出 SDIHODSHG 9 分析&#xff1a…

课堂练习4.2:页式内存管理

4-3 课堂练习4.2:页式内存管理 创建一个进程(创建进程是在磁盘中),进程以字节为单位编号,然后再进程分为许多页(每页 4KB ),内存中有对应的页框(设定同页)。通过页表(记录页和页框的对应关系),将最需要的页调入内存,其他页留在磁盘中。根据 CPU 的需要动态的更新…

鸿蒙开发之封装优化

面向对象开发离不开封装&#xff0c;将重复的可以复用的代码封装起来&#xff0c;提高开发效率。 基于之前的List&#xff0c;对代码进行封装。 1、抽取component 将List的头部抽离出来作为一个新的component。可以创建一个新的ArkTS文件&#xff0c;写我们的头部代码 为了…

Springboot获取jar版本方法

Springboot获取jar版本方法 方案一: 通过jar的pom.properties文件获取 获取demo Properties properties new Properties(); try {properties.load(RakicAppInfo.class.getResourceAsStream("/META-INF/maven/com.rakic.framework/rakic-app-springboot-start/pom.pro…

Kubernetes里的DNS;API资源对象ingress;Kubernetes调度;节点选择器NodeSelector;节点亲和性NodeAffinity

Kubernetes里的DNS K8s集群内有一个DNS服务&#xff1a; kubectl get svc -n kube-system |grep dns测试&#xff1a; 在tang3上安装bind-utils,目的是安装dig命令 yum install -y bind-utils apt install dnsutils #ubuntu上 解析外网域名 dig 10.15.0.10 www.baidu.com…
最新文章