Linux多进程(二)进程通信方式一 管道

news/2024/10/22 12:19:10/

管道的是进程间通信(IPC - InterProcess Communication)的一种方式,管道的本质其实就是内核中的一块内存(或者叫内核缓冲区),这块缓冲区中的数据存储在一个环形队列中,因为管道在内核里边,因此我们不能直接对其进行任何操作。

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

  • 管道对应的内核缓冲区大小是固定的,默认为4k(也就是队列最大能存储4k数据)
  • 管道分为两部分:读端和写端(队列的两端),数据从写端进入管道,从读端流出管道
  • 管道中的数据只能读一次,做一次读操作之后数据也就没有了(读数据相当于出队列)
  • 管道是单工的:数据只能单向流动, 数据从写端流向读端
  • 对管道的操作(读、写)默认是阻塞的

一、匿名管道

匿名管道是管道的一种,既然是匿名也就是说这个管道没有名字,但其本质是不变的,就是位于内核中的一块内存,匿名管道拥有上面介绍的管道的所有特性。

pipe 函数用于创建一个管道,以实现进程间通信。

#include <unistd.h>int pipe(int fd[2]);

管道容量的大小默认是65536字节。我们可以使用fcntl函数来修改管道容量。

下面举一个在多进程程序中管道通信的例子:

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>int main()
{int pipefd[2];char buf[1024];// 创建管道if (pipe(pipefd) == -1){perror("pipe error! \n");return 1;}for (int i = 0; i < 8; i++){// 创建子进程pid_t pid = fork();if (pid == -1){perror("fork error!\n");return 1;}else if (pid == 0){// 子进程关闭写端close(pipefd[1]);// 从管道读取数据memset(buf, 0, 1024);read(pipefd[0], buf, sizeof(buf));// 打印数据printf("pid = %d :Child process received: %s\n", getpid(), buf);// 子进程关闭读取端close(pipefd[0]);// 子进程直接break,以免创建更多的子进程break;}}if (getpid() != 0){// 父进程关闭读取端close(pipefd[0]);// 向管道写入数据const char *message = "Hello from parent process";for (int i = 0; i < 8; i++){write(pipefd[1], message, strlen(message));sleep(1);}// 在所有数据都写入后再关闭写入端close(pipefd[1]);}return 0;
}

生成了八个子进程,八个子进程阻塞在read处,等待父进程的消息,父进程发了八次消息,每次间隔1秒。看一下仿真
请添加图片描述

二、有名管道

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

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

2.1、创建有名管道

通过命令

mkfifo 有名管道的名字

通过函数

#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);
  • pathname: 要创建的有名管道的名字
  • mode: 文件的操作权限, 和open()的第三个参数一个作用,最终权限: (mode & ~umask)
  • 返回值:创建成功返回 0,失败返回 -1

2.2、进程间通信

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

有名管道操作需要通过 open() 操作得到读写管道的文件描述符,如果只是读端打开了或者只是写端打开了,进程会阻塞在这里不会向下执行,直到在另一个进程中将管道的对端打开。

写管道的进程

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>int main()
{// 1. 创建有名管道文件int ret = mkfifo("./testfifo", 0664);if(ret == -1){perror("mkfifo");exit(0);}printf("管道文件创建成功...\n");// 2. 打开管道文件int wfd = open("./testfifo", O_WRONLY);if(wfd == -1){perror("open");exit(0);}printf("以只写的方式打开文件成功...\n");// 3. 循环写管道int i = 0;while(i<100){char buf[1024];sprintf(buf, "hello, fifo, 我在写管道...%d\n", i);write(wfd, buf, strlen(buf));i++;sleep(1);}close(wfd);return 0;
}

读管道的进程

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>int main()
{// 1. 打开管道文件int rfd = open("./testfifo", O_RDONLY);if(rfd == -1){perror("open");exit(0);}printf("以只读的方式打开文件成功...\n");// 2. 循环读管道while(1){char buf[1024];memset(buf, 0, sizeof(buf));// 读是阻塞的, 如果管道中没有数据, read自动阻塞int len = read(rfd, buf, sizeof(buf));printf("读出的数据: %s\n", buf);if(len == 0){// 写端关闭了, read解除阻塞返回0printf("管道的写端已经关闭, 拜拜...\n");break;}}close(rfd);return 0;
}

仿真结果

请添加图片描述


http://www.ppmy.cn/news/1442468.html

相关文章

牛客NC99 多叉树的直径【较难 深度优先 Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/a77b4f3d84bf4a7891519ffee9376df3 思路 核心就是树的最大直径(globalMax)一定是以某一个node为root最长的两个path-to-leaf. 就是普通dfs的同时算路径长度。时间: O(n), DFS一次 空间: O(n)参考答案Java impo…

如何使用 Internet Download Manager (IDM) 来加速和优化你的下载体验 IDM 6.41下载神器

在当今信息爆炸的时代&#xff0c;下载文件和媒体内容已成为我们日常生活的一部分。无论是工作学习还是娱乐休闲&#xff0c;我们都需要从互联网上下载各种资源。为了提高下载效率和确保文件完整性&#xff0c;选择一款优秀的下载管理软件至关重要。Internet Download Manager …

[docker] volume 补充 环境变量 参数

[docker] volume 补充 & 环境变量 & 参数 这里补充一下 volume 剩下的内容&#xff0c;以及添加参数(ARG) 和 环境变量 ENV 的内容 read only volumes ❯ docker run-p 3000:80--rm--name feedback-app-v feedback:/app/feedback-v "$(pwd):/app"-v /app/…

SQLite导出数据库至sql文件

SQLite是一款实现了自包含、无服务器、零配置、事务性SQL数据库引擎的软件库。SQLite是世界上部署最广泛的SQL数据库引擎。 SQLite 是非常小的&#xff0c;是轻量级的&#xff0c;完全配置时小于 400KiB&#xff0c;省略可选功能配置时小于250KiB。 SQLite 源代码不受版权限制。…

Node.js使用

Node.js是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;它使得JavaScript能够脱离浏览器&#xff0c;直接在服务器端运行。Node.js的异步I/O模型使其在处理高并发请求时表现出色&#xff0c;适用于构建网络应用、实时应用等。以下是对Node.js使用的总结&#xff1a; …

C++初识内存管理和模版

目录 前言 1.C/C内存分布 2. C的内存管理方式 2.1 new/delete操作内置类型 2. new和delete操作自定义类型 3. operator new和operator delete函数 4. new和delete的实现原理 4.1 内置类型 4.2 自定义类型 5. malloc/free和new/delete的区别 6. 初识模版 6.1 泛型编…

Spring从零开始学使用系列(三)--依赖注入(DI)

目录 1.DI的核心概念 1.1优势 2. Spring中的DI实现 2.1 构造器注入 2.1.2 优势和缺点 2.2 设置器注入 2.2.1 如何使用设置器注入 2.2.2 示例代码 2.2.3优势和使用场景 2.3 字段注入 2.4 方法注入 2.4.1 方法注入的概念 2.4.2 找方法注入 2.4.3 Lookup 注解的作用 2…

贪心算法练习day.5

435.无重叠区间 链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重叠 。 示例 1: 输入: intervals…