Linux应用编程---6.无名管道

news2024/11/19 1:28:34

Linux应用编程—6.无名管道

6.1 pipe()函数使用详情

​ 管道是进程之间进行通讯的一种方式。管道有两种,分别是:无名管道和有名管道。先看无名管道。管道创建函数是pipe(),在Linux终端输入man pipe。

image-20221124232548549

图1 pipe()函数详情
NAME
       pipe, pipe2 - create pipe

SYNOPSIS
       #include <unistd.h>

       /* On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64; see NOTES */
       struct fd_pair {
           long fd[2];
       };
       struct fd_pair pipe();

       /* On all other architectures */
       int pipe(int pipefd[2]);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <fcntl.h>              /* Obtain O_* constant definitions */
       #include <unistd.h>

       int pipe2(int pipefd[2], int flags);

​ pipe()与pipe2()函数用来创建管道。调用这两个函数需要包含unistd.h这个头文件。pipe()函数的入参是一个有两个元素的整形数组。

ESCRIPTION
       pipe()  creates a pipe, a unidirectional data channel that can be used for interprocess communication.  The array pipefd is used to return two file descriptors referring
       to the ends of the pipe.  pipefd[0] refers to the read end of the pipe.  pipefd[1] refers to the write end of the pipe.  Data written to the write end  of  the  pipe  is
       buffered by the kernel until it is read from the read end of the pipe.  For further details, see pipe(7).

​ pipe()函数创建管道,管道是一个单向的数据通道被用于进程间通讯。这个数组pipefd被用于返回两个文件描述符。 pipefd[0] 数组第一个元素是管道读文件描述符,pipefd[1]数组第二个元素是管道写 文件描述符。写入管道写端的数据由内核进行缓存,直到管道读端读取。

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, errno is set appropriately, and pipefd is left unchanged.

​ 管道创建成功的话,函数返回0,如果失败,则返回-1。

6.2 pipe()编程实战

​ 创建父子进程,父子进程之间通过管道传递数据,比如字符串。如何给父子进程创建管道,因为调用fork()函数创建子进程时,子进程是父进程的拷贝,所以,父进程先创建了管道,子进程也拥有了管道。但是,对于父子进程的管道来说,这个管道都有一个数据的读取端与写入端。如何使数据单向传递呢,我们在父进程中,将管道的读取端关闭,只使用管道的写入端,来向子进程传递数据。在子进程的管道中,我们关闭管道的写入端,只使用管道的读取端,来接收父进程的数据。

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

int main(void)
{
        int fd[2];
        int ret = 0;
        pid_t pid;

        ret = pipe(fd);

        if(0 != ret)
        {
                perror("pipe.");
        }

        pid = fork();

        if(pid > 0)                     // Parent process.
        {
                close(fd[0]);
                sleep(5);               // Delay 5 seconds.
                write(fd[1], "123456789", 9);   // Write data:vae, length = 3.

                while(1);               // Keep parent process running.
        }
        else if(pid == 0)               // child process.
        {
                char str[9];            // Read data buffer.

                printf("Waitting parent process data...\n");
                close(fd[1]);           // In child process, close write end of the pipe.
                read(fd[0], str, 9);    // Read data from fd[0];
                printf("The data from Parent process is %s.\n", str);
        }
        else
        {
                perror("fork.");
        }

        return 0;
}

​ 运行结果:

image-20221125001242338

图2 运行结果

​ 子进程先打印提示信息,5秒钟后(父进程在关闭管道读取端后,延时5秒发送数据),子进程管道读取端读取数据,并且打印了出来。

​ 疑问:邴老师留下了问题,管道能传递的数据有限制嘛?如果有,是多少。编写一个测试代码。

6.3 测试无名管道的大小

​ 编写一段代码,用来测试管道大小。原理就是,在子进程中一直往管道的写入端写数据,并且对写操作进行计数,当管道内无法写入时,代码就阻塞了。此时,父进程调用waitpid()函数等待子进程的结束。

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>

int main(void)
{
        int ret = 0;
        int fd[2];
        pid_t pid;

        ret = pipe(fd);

        if(-1 == ret)
        {
                perror("pipe");
        }

        pid = fork();

        if(pid == 0)            // Child process: write data to the end of pipe.
        {
                char ch = '!';
                int count = 0;

                close(fd[0]);

                while(1)
                {
                        write(fd[1], &ch, 1);			// 每次写入1个字节。
                        printf("Write data count = %d.\n", ++count);
                }
        }
        else if(pid > 0)		// Parent process: wait until child process is over.
        {
                waitpid(pid, NULL, 0);
        }

        return 0;
}

​ 运行结果:

image-20221125011239798

图3 运行结果

​ 可以看出,当子进程往管道写数据写到65536时,无法在继续写入。说明管道的大小是65536个字节。

6.4 管道应用编程

​ 编写一段代码,创建一个父子进程,子进程不断接收用户键盘输入的字符串数据,并通过管道传递到父进程,父进程收到数据就要打印出来。

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

int main(void)
{
        int ret = 0;
        int fd[2];
        pid_t pid;

        ret = pipe(fd);

        if(-1 == ret)
        {
                perror("pipe.");
        }

        pid = fork();

        if(pid == 0)
        {
                char str[32];

                close(fd[0]);
                while(1)
                {
                        scanf("%s", str);
                        write(fd[1], &str, sizeof(str));
                }
        }
        else if(pid > 0)
        {
                char str[32];

                close(fd[1]);
                while(1)
                {
                        printf("Waitting data from child process...\n");
                        read(fd[0], &str, sizeof(str));
                        printf("Data from child process is %s.\n", str);
                }
        }
        else
        {
                perror("fork.");
        }

        return 0;
}

​ 运行结果:

image-20221125103641362

图4 运行结果

​ 现在可以实现父子进程之间连续通过管道传递数据。

6.5 双向管道在父子进程之间传递数据

​ 之前的代码,父子进程通过管道单向传递数据。有点类似半双工通讯,只能一方传递给另外一方。现在代码实现这样一个场景,父进程通过管道向子进程传递一串不定长的字符串,子进程接收到字符串后,将其修改为大写,然后在传递到父进程,供父进程打印。这里父进程有写入数据的操作,也有读取数据的操作。子进程有读取数据的操作,也有写入数据的操作。由于管道是单向的,需要创建2根管道。

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

int main(void)
{
        int ret = 0;
        int fd1[2];
        int fd2[2];
        pid_t pid;

        ret |= pipe(fd1);
        if(0 != ret)
        {
                perror("pipe.");
        }

        ret |= pipe(fd2);
        if(0 != ret)
        {
                perror("pipe.");
        }

        pid = fork();

        if(pid > 0)
        {
                char buff[64];

                close(fd1[0]);
                close(fd2[1]);

                while(1)
                {
                        memset(buff, '\0', sizeof(buff));
                        gets(buff);

                        write(fd1[1], buff, sizeof(buff));

                        memset(buff, '\0', sizeof(buff));
                        read(fd2[0], buff, sizeof(buff));
                        printf("After change is : %s.\n", buff);
                }
        }
       else if(pid == 0)
        {
                int i = 0;
                char buff[64];

                close(fd1[1]);
                close(fd2[0]);

                while(1)
                {
                        memset(buff, '\0', sizeof(buff));

                        read(fd1[0], buff, sizeof(buff));
                        for(i = 0; i < sizeof(buff); i++)
                        {
                                buff[i] = toupper(buff[i]);
                        }

                        write(fd2[1], buff, sizeof(buff));
                }
        }
        else
        {
                perror("fork.");
        }


        return 0;
}

​ 运行结果:

image-20221125202915929

图5 运行结果

​ 示意图如下:

image-20221125204419748

图6 双向管道示意图

6.6 总结

​ 管道用于进程之间通讯,管道又分为有名管道和无名管道。管道是单向的,创建管道用函数pipe(),需要传入一个数组int fd[2],有2个元素,元素类型是整形。其中,fd[0]是管道读取端, fd[1]是管道写入端。它两叫做文件通配符,可以使用函数close()与write()进行操作。
在这里插入图片描述

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

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

相关文章

Mathorcup数学建模竞赛第六届-【妈妈杯】B题:小区车位分布的优化设计与评价(附一等奖获奖论文、C++和matlab代码)

赛题描述 随着现代社会经济的快速发展,房地产成为国家经济发展中重要的经济增长点之一。而小区内汽车停车位的分布对于小区居民的上下班出行影响很大。请建立数学模型,解决下列问题: 问题1:分析评判小区汽车停车位分布是否合理的几个关键指标,建立评判车位分布合理的数学…

mybatis官方文档之第一个mybatis程序实操

mysql数据库准备工作&#xff1a; 首先&#xff0c;我们需要在mysql数据库中&#xff0c;建立数据库和数据表&#xff1a; //创建数据库 create database my_batis; //创建表 create table students(id int not null,name varchar(30) default null,pwd varchar(30) default …

用移动激光扫描来估计树干直径的分割和树干校准法

Paper题目&#xff1a;Mobile Laser Scanning for Estimating Tree Stem Diameter Using Segmentation and Tree Spine Calibration Abstract 移动激光扫描 (MLS) 可以通过使用自动推导出树干中心位置和树干直径的算法来提高森林清查效率。在这项工作中&#xff0c;我们提出了…

远程控制软件– 向日葵使用教程

新冠疫情的严峻形式下&#xff0c;各地都延期复工&#xff0c;一时间远程办公&#xff0c;在家办公可谓非常火爆。然而&#xff0c;家里毕竟不是公司&#xff0c;很多的资料都在办公室电脑&#xff0c;甚至一些比较专业系统或者专属网络限制无法完成在家办公&#xff0c;这时候…

从加(解)密角度讲栅栏密码

目录普通型栅栏加密原理解密原理W型栅栏加密原理实例解密原理这今天在做新生赛的一道“只有倒着翻过十八层的篱笆才能抵达北欧神话的终点”&#xff0c;研究了很长时间的栅栏原理&#xff0c;flag没出来&#xff0c;而且自己对于普通和W型的加密解密整的有的蒙… 原先一篇文章里…

sentinel-流量控制

github地址&#xff1a;主页 alibaba/Sentinel Wiki GitHub 目录 概述基于 QPS/并发数的流量控制基于调用关系的流量控制 概述 流量控制&#xff08;flow control&#xff09;&#xff0c;其原理是监控应用流量的 QPS 或并发线程数等指标&#xff0c;当达到指定的阈值时对…

免费安全的内网穿透实现——Tailscale

一、需求说明想要实现访问公司或家里的网络设备&#xff08;Windows电脑、NAS、安卓设备等&#xff09;;但是这些设备又没有对应的公网IP地址&#xff1b;且就算有公网 IP 地址&#xff0c;也不放心让这些网络设备直接公开暴露在网络环境中&#xff08;这样很容易被频繁的恶意扫…

【MySQL Tips】除了指定密码外 MySQL客户端更好的选择——登录路径

在实际项目中&#xff0c;我们在生产&#xff08;环境&#xff09;发布新版本或运维时&#xff0c;使用 mysql 、mysqladmin、mysqlimport、mysqldump、mysqlpump 等 MySQL 客户端程序时&#xff0c;每次都需要输入密码&#xff0c;一般都会采用更安全的互动输入密码模式&#…

UOS 22.0家庭版使用体验

1月4日这天我收到了UOS的22.0版本的推送&#xff0c;更新后新增了未成年人账户和学习中心&#xff0c;由于是虚拟机中所以无法新增未成年人账户&#xff0c;于是我制作了Linux to go&#xff08;LTG&#xff09;但是由于我的U盘不是固态U盘所以我用普通的更新就卡在了更新界面。…

Linux下第一个程序:进度条

一、前言 进度条程序曾经是百度的一道面试题。 这一期博客我们来介绍代码和原理究竟是怎样的。 二、知识点 1.回车和换行 通常意义上我们以为回车就是就是键盘的那个回车键。 其实不然。回车其实是不换行&#xff0c;回到本行开头。我们通常用符号表示为 \r 。 换行就是新…

Docker Compose安装

目录 前言 1. 二进制包在线安装 2. 二进制包离线安装 2.1 下载安装包。 2.2 上传到linux服务器 2.3 解压到/usr/local/bin/docker-compose目录。 2.4 将可执行权限应用于二进制文件 2.5 创接建软链&#xff08;可选&#xff09; 2.6 测试是否安装成功 3. 国内镜像二进…

Linux应用编程---3.wait()函数

Linux应用编程—3.wait()函数 ​ 首先引入三个函数&#xff0c;我们通过在Linux终端下查阅它的作用与使用方法。 ​ Linux终端命令下输入&#xff1a;man exit&#xff0c;敲击回车键即可打开exit函数详情页。 图1 exit编程手册​ exit函数的作用是终止一般进程&#xff0c;没…

12、Java基础之泛型的使用

一、泛型的理解1、泛型的概念所谓泛型&#xff0c;就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时&#xff08;例如&#xff0c; 继承或实现这个接口&#xff0c;用这个类型声明变量、创建对象时&#…

[游戏测试]基于人工智能博弈树,极大极小(Minimax)搜索算法并使用Alpha-Beta剪枝算法优化实现的可人机博弈的AI智能五子棋游戏。

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; (*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &a…

关于idea中查看源码时的注释以及.class与.java文件的问题

文章目录问题描述解决方法问题描述 在使用idea编辑器学习java的时候发现有的人的idea将鼠标方法java自带的类方法上会出现解释注释&#xff0c;但是我的idea不可以&#xff0c;经过查询发现是idea中jdk选择的问题。 下图为能查看注释时的截图 按住ctrl点击方法名进入&#x…

分治和递归

目录 分治的概念&#xff1a; 递归的概念&#xff1a; 分治策略的特征&#xff1a; 分治法步骤&#xff1a; 例&#xff1a;阶乘&#xff01; 迭代 递归 关于递归使用栈 斐波拉切数列 迭代 递归 分治的概念&#xff1a; 将一个难以直接解决的大问题&#xff08;规模大…

【年终总结】我的前端之行,回顾2022,展望2023

&#x1f431;个人主页&#xff1a;不叫猫先生 &#x1f64b;‍♂️作者简介&#xff1a;前端领域新星创作者、华为云享专家、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab;系列专栏&#xff…

Vivado综合属性之MAX_FANOUT

本文介绍了综合属性MAX_FANOUT对Schematic的影响&#xff0c;通过本文可以理解通过寄存器复制的方式可以降低扇出。 高扇出信号可能会因为布线拥塞而出现时序问题。常用的规避方法是通过寄存器复制的方式降低扇出&#xff0c;可通过MAX_FANOUT实现寄存器复制。 MAX_FANOUT既可…

为金融业保驾护航,浪潮存储容灾方案获得权威媒体认可

近日&#xff0c;在2022中国金融科技年会上&#xff0c;经权威IT专家多项严格评审&#xff0c;浪潮金融行业数据存储与容灾解决方案&#xff0c;凭借安全、可靠、经济、高效四大优势&#xff0c;能够满足金融业务服务永远在线、数据永不丢失、性能永远满足、容量永远充足的核心…

【Linux】Linux编译器 gcc 的使用 | 动静态库的初步认识

&#x1f451;作者主页&#xff1a;进击的安度因 &#x1f3e0;学习社区&#xff1a;进击的安度因&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;Linux 文章目录一、前言二、gcc 演示翻译环境1、预处理2、编译3、汇编4、链接5、总结三、动静态链接库1、库…