进程管道:pipe调用

news2025/1/13 7:50:40

在看过高级的popen函数之后,我们再来看看底层的pipe函数。通过这个函数在两个程序之间传递数据不需要启动一个shell来解释请求的命令。它同时还提供了对读写数据的更多控制。pipe函数的原型如下所示:

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

 pipe函数的参数是一个由两个整数类型的文件描述符组成的数组的指针。该函数在数组中填上两个新的文件描述符后返回0,如果失败则返回-1并设置errno来表明失败的原因。在Linux手册页(手册的第二部分)中定义了下面一些错误。

❑ EMFILE:进程使用的文件描述符过多。

❑ ENFILE:系统的文件表已满。

❑ EFAULT:文件描述符无效。 

两个返回的文件描述符以一种特殊的方式连接起来。写到file_descriptor[1]的所有数据都可以从file_descriptor[0]读回来。数据基于先进先出的原则(通常简写为FIFO)进行处理,这意味着如果你把字节1,2,3写到file_descriptor[1],从file_descriptor[0]读取到的数据也会是1, 2,3。这与栈的处理方式不同,栈采用后进先出的原则,通常简写为LIFO。 

特别要注意,这里使用的是文件描述符而不是文件流,所以我们必须用底层的read和write调用来访问数据,而不是用文件流库函数fread和fwrite。

下面的程序pipe1.c用pipe函数来创建一个管道。

实验pipe函数 

注意file_pipes数组的用法,它的地址被当作参数传递给pipe函数。

测试代码:

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

#define DEBUG_INFO(format, ...) printf("%s - %d - %s :: "format"\n",__FILE__,__LINE__,__func__ ,##__VA_ARGS__)

void test_01(void){
    int len = 0;
    int fds_pipes[2];
    const char some_data[] = "123";
    const char some_data2[] = "456";

    char buf[BUFSIZ + 1];

    memset(buf,0,sizeof(buf));

    if(pipe(fds_pipes) == 0){
        len = write(fds_pipes[1],some_data,strlen(some_data));
        DEBUG_INFO("write len = %d",len);
        len = read(fds_pipes[0],buf,BUFSIZ);
        DEBUG_INFO("read len = %d,buf = %s",len,buf);

        memset(buf,0,sizeof(buf));
        len = write(fds_pipes[0],some_data2,strlen(some_data2));
        DEBUG_INFO("write len = %d",len);
        len = read(fds_pipes[1],buf,BUFSIZ);
        DEBUG_INFO("read len = %d,buf = %s",len,buf);
    }else{
        perror("pipe:");
    }
}

int main(int argc, char**argv){
    test_01();
    DEBUG_INFO("hello world");
    return 0;
}

测试结果:

 实验解析

这个程序用数组file_pipes[]中的两个文件描述符创建一个管道。然后它用文件描述符file_pipes[1]向管道中写数据,再从file_pipes[0]读回数据。注意,管道有一些内置的缓存区,它在write和read调用之间保存数据。

如果你尝试用file_descriptor[0]写数据或用file_descriptor[1]读数据,其后果并未在文档中明确定义,所以其行为可能会非常奇怪,并且随着系统的不同,其行为可能会发生变化。在我的系统上,这样的调用将失败并返回-1,这至少能够说明这种错误比较容易发现。

乍看起来,这个使用管道的例子并无特别之处,它做的工作也可以用一个简单的文件完成。管道的真正优势体现在,当你想在两个进程之间传递数据的时候。当程序用fork调用创建新进程时,原先打开的文件描述符仍将保持打开状态。如果在原先的进程中创建一个管道,然后再调用fork创建新进程,我们即可通过管道在两个进程之间传递数据。

实验 跨越fork调用的管道

(1)下面这个程序pipe2.c的开始部分(在调用fork之前的部分)和第一个例子非常相似。

测试代码:

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

#define DEBUG_INFO(format, ...) printf("%s - %d - %s :: "format"\n",__FILE__,__LINE__,__func__ ,##__VA_ARGS__)

void test_01(void){
    int len = 0;
    int fds_pipes[2];
    const char some_data[] = "123";
    const char some_data2[] = "456";
    pid_t pid;
    int res;

    char buf[BUFSIZ + 1];

    memset(buf,0,sizeof(buf));
    res = pipe(fds_pipes);
    if(res != 0){
        perror("pipe:");
        return;
    }
    pid = fork();
    if(pid < 0){
        perror("fork:");
        return ;
    }
    if(pid == 0){
        len = read(fds_pipes[0],buf,BUFSIZ);
        DEBUG_INFO("CHILD:pid = %lu,read -- len = %d,buf = %s\n",getpid(),len,buf);

        len = write(fds_pipes[1],some_data2,BUFSIZ);
        DEBUG_INFO("CHILD:pid = %lu,write -- len = %d\n",getpid(),len);

        sleep(1);
        return;
    }
    len = write(fds_pipes[1],some_data,BUFSIZ);
    DEBUG_INFO("PARENT:pid = %lu,write -- len = %d\n",getpid(),len);

    len = read(fds_pipes[0],buf,BUFSIZ);
    DEBUG_INFO("PARENT:pid = %lu,read -- len = %d,buf = %s\n",getpid(),len,buf);
    sleep(1);
}

int main(int argc, char**argv){
    test_01();
    DEBUG_INFO("hello world");
    return 0;
}

测试结果:

在本测试代码中实现了双向通信。

实验解析

这个程序首先用pipe调用创建一个管道,接着用fork调用创建一个新进程。如果fork调用成功,父进程就写数据到管道中,然后父进程开发等待从管道中读数据,子进程从管道中读取数据后,向管道中写数据,然后父进程读到数据。父进程和子进程写数据的时候都使用的文件描述符fds_pipes[1],读数据的时候都使用的文件描述符fds_pipes[0]

 

 CMakeLists.tx 、编译脚本和sftp.json

cmake_minimum_required(VERSION 3.8)
project(myapp)

# add_compile_options("-std=c++11")

add_executable(popen popen.c)
add_executable(popen2 popen2.c)
add_executable(popen3 popen3.c)
add_executable(popen4 popen4.c)
add_executable(pipe1 pipe1.c)
add_executable(pipe2 pipe2.c)
rm -rf _build_
mkdir _build_ -p
cmake -S ./ -B _build_
make -C _build_
# ./_build_/popen
# ./_build_/popen2
# ./_build_/popen3
# ./_build_/popen4
# ./_build_/pipe1
./_build_/pipe2

 sftp.json

{
    "name": "My Server",
    "host": "192.168.31.138",
    "protocol": "sftp",
    "port": 22,
    "username": "lkmao",
    "password": "lkmao",
    "remotePath": "/big/work/ipc",
    "uploadOnSave": true,
    "useTempFile": false,
    "openSsh": false
}

小结

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

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

相关文章

OS的事件机制-Event

Event在OSEK OS中&#xff0c;其实就是一个flag的作用&#xff0c;如果某个TASK执行了&#xff0c;就调用<SetEvent()>把flag就置起来&#xff0c;和这个task关联的另一个TASK也执行了&#xff0c;就可以把flag清掉<ClearEvent>&#xff0c;如果第一个TASK没有执行…

这才是你想了解的Redis

文章简介 redis作为一个基于内存的数据结构存储系统&#xff0c;由于它的灵活性和可拓展性强&#xff0c;在我们日常开发中经常被用作数据库、缓存或者消息代理。本文就从Redis的基本部署使用说到Redis的集群、锁和消息对列 Redis基本使用 一、安装 下载地址&#xff1a;htt…

mySql 储存过程 多个结果返回解析

当需要查询复杂的数据模型并返回多个结果集时&#xff0c;使用 MySQL 存储过程可以有效地优化性能。同时&#xff0c;在开发中使用 Mybatis 可以方便地调用 MySQL 存储过程并获取多个结果集。本文将介绍如何在 Mybatis 中调用 MySQL 存储过程&#xff0c;并获取多个结果集。 1、…

Java并发之原子类

一、原子类简介 1 什么是原子类 Java中提供了一些原子类&#xff0c;原子类包装了一个变量&#xff0c;并且提供了一系列对变量进行原子性操作的方法。原子性的意思是对于一组操作&#xff0c;要么全部执行成功&#xff0c;要么全部执行失败&#xff0c;不能只有其中某几个执…

基础知识学习---牛客网C++面试宝典(三)C/C++基础之面向对象

1、本栏用来记录社招找工作过程中的内容&#xff0c;包括基础知识学习以及面试问题的记录等&#xff0c;以便于后续个人回顾学习&#xff1b; 暂时只有2023年3月份&#xff0c;第一次社招找工作的过程&#xff1b; 2、个人经历&#xff1a; 研究生期间课题是SLAM在无人机上的应…

测试新手百科:Postman简介、安装、入门使用方法详细攻略!

本文关键词&#xff1a;Postman基础 目录 一、Postman背景介绍 二、Postman的操作环境 三、Postman下载安装 四、Postman的基础功能 五、接口请求流程 六、管理用例—Collections 七、身份验证Authentication 一、Postman背景介绍 用户在开发或者调试网络程序或者是网…

【答题】在线答卷-答题系统的微信小程序开发流程详解

用死记硬背的方法学习的学生&#xff0c;面对桌上堆积成厚厚的书本&#xff0c;是否感觉鸭梨山大呢&#xff0c;想着教育却面临着学习成本不小问题&#xff0c;是否感觉各种不便呢&#xff0c;如果对编程代码有感兴趣&#xff0c;不妨试试做一个自己的在线答题系统&#xff0c;…

有效性常见标志词

有效性常见标志词 混淆概念常见标志词 &#xff08; 1 &#xff09; 既然…那么… &#xff08; 2 &#xff09; 也就是说… &#xff08; 3 &#xff09; 很显然… &#xff08; 4 &#xff09; 因为A 就是B…所以… &#xff08; 5 &#xff09; 某主体A 是 &#xff0c;…

低代码01之构建项目框架

目录 低代码之构建框架11&#xff1a;项目初始化2&#xff1a;src / data.json 数据 &#xff08; 容器大小与渲染的表单数据 &#xff09;3&#xff1a;App.vue ( 导入editor组件传递data.json之中的数据与 向下提供组件配置数据config )4&#xff1a;src / packages / editor…

我记不住的那些C语言的二维数组的函数传参

背景&#xff1a; 最近在复习数据结构和算法&#xff0c;顺带刷刷题&#xff0c;虽然很长时间不刷题了但还是原来熟悉的味道&#xff0c;每一次重学都是加深了上一次的理解。本次我们看一下如何将C语言的二维数组进行函数传参&#xff0c;C语言实现。 其实这个比较简单&#x…

springboot项目使用proguard配置代码混淆

springboot项目使用proguard配置代码混淆 代码混淆是一些软件开发过程中必不可少的步骤。 常用的代码混淆技术有 proguard maven plugin , yguard maven plugin, procyon maven plugin, dex maven plugin . 这些代码混淆技术大同小异&#xff0c;都是对maven打包生成class时进…

补充知识点

这里写目录标题 进制转换Java内置的进制转换介绍具体代码 有符号数据表示法整数强制转换之数据溢出浮点数进制转换浮点数储存 进制转换 Java内置的进制转换 介绍 也就是常用API里Integer的静态方法 具体代码 注意 最后一个方法&#xff0c;返回的是基于第二个参数为基数的第…

大模型部署实战(三)——ChatGLM-6B

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

SSR渲染-初识Nuxt-01

SSR服务端渲染 SSR服务端渲染&#xff1a;在后端将html页面处理好&#xff0c;前端直接展示&#xff08;可以解决为后端给你传了一个html脚本&#xff0c;全段渲染&#xff09; 为什么要有SSR服务端渲染&#xff1f; 可以解决单页面首屏加载慢的问题&#xff0c;同时有利于用…

在线分享怎么多接口批量查询快递信息

做物流和电商行业的小伙伴应该都知道&#xff0c;大量快递集中发出后&#xff0c;我们要做的就是及时查询和跟踪快递单号&#xff0c;这样能够有效避免快递发错或快递丢失等情况出现&#xff0c;小编今天给大家安利一款全自动批量查询跟踪快递单号的辅助工具&#xff0c;它支持…

【C++/嵌入式笔试面试八股】二、21.分层模型 | HTTP

分层模型 01.画出OSI和TCP/IP协议栈的对应关系 对应关系记忆2113 02.什么是OSI七层模型?每层列举2个协议。❤️ OSI七层模型及其包含的协议如下 物理层: 传输单位为bit 功能:通过物理媒介透明的传输比特流,确定机械及电气规范 主要包括的协议为:IEE802.3 CLOCK RJ45 数据链…

[学习笔记] [机器学习] 10. 支持向量机 SVM(SVM 算法原理、SVM API介绍、SVM 损失函数、SVM 回归、手写数字识别)

视频链接数据集下载地址&#xff1a;无需下载 学习目标&#xff1a; 了解什么是 SVM 算法掌握 SVM 算法的原理知道 SVM 算法的损失函数知道 SVM 算法的核函数了解 SVM 算法在回归问题中的使用应用 SVM 算法实现手写数字识别器 1. SVM 算法简介 学习目标&#xff1a; 了解 …

路径规划算法:基于阿基米德优化优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于阿基米德优化优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于阿基米德优化优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用…

Spring Bean-生命周期

三连支持 一起鼓励 一起进步 Bean生命周期 文章目录 一、生命周期1.Bean中配置生命周期2.实现InitializingBean和DisposableBean接口3.PostConstruct & PreDestroy4.BeanPostProcessor接口 二、执行过程三、源码中使用的BeanPostProcessor1.以ApplicationContextAwareProce…

【Flutter】Flutter 如何实现主题 Theme 切换

文章目录 一、引言二、Flutter 中的主题&#xff08;Theme&#xff09;和主题数据&#xff08;ThemeData&#xff09;三、如何在 Flutter 中创建自定义主题四、在 Flutter 中实现主题切换五、完整的代码示例六、总结 一、引言 大家好&#xff0c;欢迎阅读这篇文章。今天我们要…