Linux高级系统编程 - 5 管道

复制文件描述符

dup函数

作用 : 文件描述符复制
语法
        #include <unistd.h>
        int dup(int oldfd);
参数 :
        所需复制的文件描述符
返回值
        复制得到的文件描述符
功能 : 从文件描述符表中 , 寻找一个最小可能的文件描述符(通过返回值返回)作为 oldfd复制
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{int newFd = dup(1);write(newFd, "hello world", 11);return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{int fd = open("a.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);close(1);dup(fd);printf("啦啦啦,德玛西亚");close(fd);return 0;
}

dup2函数(推荐)

#include <unistd.h>
int dup2(int oldfd, int newfd);
参数 :
        oldfd:原文件描述符
        newfd:指定复制到的文件描述符 , 如果该文件描述符存在 , 那么将原有的关闭
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{int fd = open("a.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd, 1);printf("123");return 0;
}

无名管道

又名管道 (pipe)
        无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符,1 个描述符写 fd[1], 1 个描述符读 fd[0]
核心 :0 1
特点:
        1,管道不是普通的文件 , 不属于某个文件系统 , 其只存在于内存中。
        2,半双工,数据在同一时刻只能在一个方向上流动
补充
        单工: 指数据传输只支持数据在一个方向上传输
        双工: 指二台通讯设备之间,允许有双向的资料传输
        全双工: 允许二台设备间同时进行双向数据传输。一般的电话、手机就是全双工的系统,因为在讲话时同时也可以听到对方的声音。
        半双工: 允许二台设备间进行双向数据传输 , 但不能同时进行。因此同一 时间只允许一设备传送资料,若另一设备要传送资料,需等原来传送资料的设备传送完 成后再处理。
        3,数据只能从管道的一端写入,从另一端读出。
        4,写入管道中的数据遵循先入先出的规则。
        5,管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数 据的格式,如多少字节算一个消息等
        6,管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
        7,从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间 以便写更多的数据
        8,管道没有名字,只能在具有公共祖先的进程之间使用。
补充 :
        管道可以用于任意两个或更多相关进程之间的通信,只要在创建子进程 的系列调用之前通过一个共同的祖先进程创建管道即可。
        如管道可用于一个进程和其子孙进程之间的通信。第一个进程创建管 道,然后创建子进程,接着子进程再创建第一个进程的孙子进程。
        管道通常用于两个兄弟进程之间的通信—— 它们的父进程创建了管道,并 创建两个子进程。

pipe函数

作用 : 创建无名管道
语法
        #include <unistd.h>
        int pipe(int fd[2]);
参数:
        fd 为 int 型数组的首元素地址,其存放了管道的文件描述符 fd[0] fd[1]
        fd[0]为读而打开, fd[1] 为写而打开管道。
返回值:
        成功:返回 0
        失败:返回-1
如:        int fd[2];
                pipe(fd);
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{// 1,创建管道int fd[2];pipe(fd);// 2,创建子进程,子进程与父进程具有公共祖先int pid = fork();if (pid < 0){printf("创建子进程失败");return 0;}else if (pid == 0){// 子进程,读取父进程传递的数据// 因为子进程只读取消息,所以写入无用,可以关闭close(fd[1]);char buf[128];read(fd[0], buf, sizeof(buf));printf("子进程接收到的消息:%s\n", buf);// 读取结束关闭读close(fd[0]);// 关闭子进程_exit(-1);}else if (pid > 0){// 父进程,写入数据// 因为父进程只写入消息,所以读无用,可以关闭close(fd[0]);write(fd[1], "hello gd", 8);printf("父进程发送消息完成\n");close(fd[1]);wait(NULL);}return 0;
}

读写特点

        1、默认用 read 函数从管道中读数据是阻塞的。
        2、调用 write 函数向管道里写数据,当缓冲区已满时 write 也会阻塞。管道的缓冲区的大小: 64Kb
        3、通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到 SIGPIPE 信号)退出。
        4,从管道中读数据的特点 编程时可通过 fcntl 函数设置文件的阻塞特性。设置为阻 塞:fcntl(fd, FSETFL,0); 设置为非阻塞: fcntl(fd, FSETFL, O_NONBLOCK);
示例 1 :缓冲区已满时 write 也会阻塞。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{int fd[2];pipe(fd);// 0读1写int pid = fork();if (pid == 0){close(fd[1]);sleep(2);close(fd[0]);_exit(0);}else if (pid > 0){close(fd[0]);int count = 0;for (int i = 1; i < 10000; i++){char buf[1024] = {0};write(fd[1], buf, 1024);count += 1024;printf("i=%d\tcount=%d\n", i, count);}close(fd[1]);wait(NULL);}return 0;
}
示例 2 :通信过程中,写端关闭,读端将解阻塞
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{int pd[2];pipe(pd);int pid = fork();if (pid == 0){close(pd[1]);char buf[100] = {0};printf("等待写入\n");read(pd[0], buf, 100);close(pd[0]);printf("子进程结束\n");_exit(0);}else if (pid > 0){close(pd[0]);printf("5秒后关闭写\n");sleep(5);close(pd[1]);wait(NULL);}return 0;
}
示例 3: 通信过程中 读端关闭 写端将收到 SIGPIPE 信号 退出写端进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{// 创建一个无名管道int fd[2];// 创建无名管道pipe(fd);// 创建一个进程pid_t pid = fork();if (pid == 0) // 子进程 读{// 写端 无用 可删除close(fd[1]);int i = 0;while (1){char buf[128] = "";int len = read(fd[0], buf, sizeof(buf));i++;printf("len=%d\n", len);if (i == 5)break;}// 通信完记得关闭读端close(fd[0]);}else if (pid > 0) // 父进程 写{// 读端 无用 可删除close(fd[0]);while (1){printf("父进程%u写入数据\n", getpid());write(fd[1], "hello pipe", 10);sleep(1);}// 通信完记得关闭写端close(fd[1]);wait(NULL); // 等待子进程结束}return 0;
}

综合案例

要求:使用代码实现ps -A | grep bush

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{int fd[2];pipe(fd);int i = 0;for(i = 0; i < 2; i++){pid_t pid = fork();if(pid == 0){break;}}if(i == 0){close(fd[0]);dup2(fd[1],1);execl("/bin/ps","ps","-A",NULL);_exit(0);}else if(i == 1){close(fd[1]);dup2(fd[0],0);execl("/bin/grep","grep","bash",NULL);_exit(0);}else if(i == 2){while (1){int id = waitpid(-1,NULL,WNOHANG);if(id == -1){break;}}    }return 0;
}

有名管道

又名 : 命名管道 (FIFO)
特点 :
        1、半双工,数据在同一时刻只能在一个方向上流动。
        2、写入 FIFO 中的数据遵循先入先出的规则。
        3、 FIFO 所传送的数据是无格式的,这要求 FIFO 的读出方与写入方必须事先约 定好数据的格式,如多少字节算一个消息等。
        4、 FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在 内存中。
        5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
        6、从 FIFO 读数据是一次性操作,数据一旦被读,它就从 FIFO 中被抛弃,释放 空间以便写更多的数据。
        7、当使用 FIFO 的进程退出后, FIFO 文件将继续保存在文件系统中以便以后使 用。
        8、 FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。

mkfifo函数

作用 : 创建有名管道
语法
        #include <sys/types.h>
        #include <sys/stat.h>
        int mkfifo(const char *pathname, mode_t mode);
参数 :
        pathname:文件名
        mode:文件操作模式 , 一般用 0666( 所有用户可读可写 )
返回值 :
        成功:0
        失败:-1, 一般失败是因为存在与 pathname 名相同的文件

读写特点

1open打开管道 不指定O_NONBLOCK (阻塞)

        1、 open 以只读方式打开 FIFO 时,要阻塞到某个进程为写而打开此 FIFO
        2、 open 以只写方式打开 FIFO 时,要阻塞到某个进程为读而打开此 FIFO
        3、 open 以只读、只写方式打开 FIFO 时会阻塞,调用 read 函数从 FIFO 里读数据 时 read 也会阻塞。
        4、通信过程中若写进程先退出了,则调用 read 函数从 FIFO 里读数据时不阻塞;若 写进程又重新运行,则调用 read 函数从 FIFO 里读数据时又恢复阻塞。
        5、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE 信号)退出。
        6、调用 write 函数向 FIFO 里写数据,当缓冲区已满时 write 也会阻塞。

2open打开管道 指定O_NONBLOCK (非阻塞)

        1、先以只读方式打开:如果没有进程 , 已经为写而打开一个 FIFO, 只读 open 成功, 并且 open 不阻塞。
        2、先以只写方 式打开:如果没有进程 , 已经为读而打开一个 FIFO ,只写 open 将出错返回-1
        3、 read write 读写命名管道中读数据时不阻塞。
        4、通信过程中,读进程退出后, 写进程向命名管道内写数据时,写进程也会(收到SIGPIPE 信号)退出。

3、 注意: open 函数以可读可写方式打开 FIFO 文件时的特点:

        1、 open 不阻塞。
        2、调用 read 函数从 FIFO 里读数据时 read 会阻塞。
        3、调用 write 函数向 FIFO 里写数据 , 当缓冲区已满时 write 也会阻塞

综合案例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{// 1,创建有名管道,san发si接的管道mkfifo("sanToSi", 0666);// 2,创建有名管道,si发san接的管道mkfifo("siToSan", 0666);int i = 0;for (i = 0; i < 2; i++){// 创建进程int pid = fork();if (pid == 0){// pid==0说明是子进程进入的,子进程无需在创建进程break;}}if (i == 0){int fd;
// 子进程1发送消息
#ifdef USER1fd = open("siToSan", O_WRONLY);
#endif // DEBUG
#ifdef USER2fd = open("sanToSi", O_WRONLY);
#endif // DEBUGif (fd < 0){perror("打开发送管道失败\n");_exit(-1);}while (1){char buf[128];fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = 0;write(fd, buf, sizeof(buf));printf("my:%s\n", buf);if (strcmp(buf, "886") == 0){break;}}close(fd);_exit(-1);}else if (i == 1){// 子进程2,接收消息int fd = 0;
#ifdef USER1fd = open("sanToSi", O_RDONLY);
#endif // DEBUG
#ifdef USER2fd = open("siToSan", O_RDONLY);
#endif // DEBUGif (fd < 0){perror("打开接收管道失败\n");_exit(-1);}while (1){char buf[128];int len = read(fd, buf, sizeof(buf));printf("读取的字节数:%d\n", len);if (len > 0){printf("si:%s\n", buf);if (strcmp(buf, "886") == 0){break;}}}close(fd);_exit(-1);}else if (i == 2){printf("父进程%d正在执行\n", getpid());// 父进程while (1){pid_t id = waitpid(-1, NULL, WNOHANG);if (id > 0){printf("子进程%d被回收了\n", id);}else if (id == 0){continue;}else if (id < 0){break;}}}return 0;
}
// 命令代码编译gcc 文件名 -o 生成可执行文件名 -D USERX
//-D 相当于定义一个宏

总结

无名管道与有名管道的使用场景
        1,无名管道应用与有血缘关系的进程中
        2,有名管道应用与没有血缘关系的进程中
无名管道与有名管道的区别
        1,无名管道基于内存 , 无需文件管理系统
        2,有名管道基于文件和内存 , 需要文件管理系统
dup2
        作用: 复制文件描述
        意义: 可以实现文件的重定向

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

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

相关文章

Java--作用域,构造器,this

作用域基本使用 在Java编程中&#xff0c;主要的变量就是属性&#xff08;成员变量&#xff09;和局部变量。 我们说的局部变量一般是指在成员方法中定义的变量 Java中作用域的分类 全局变量&#xff1a;也就是属性&#xff0c;作用域为整个类体 局部变量&#xff1a;也就是除了…

RHEL8_Linux访问NFS存储及自动挂载

本章主要介绍NFS客户端的使用 创建FNS服务器并通过NFS共享一个目录在客户端上访问NFS共享的目录自动挂载的配置和使用 1.访问NFS存储 前面介绍了本地存储&#xff0c;本章就来介绍如何使用网络上的存储设备。NFS即网络文件系统&#xff0c;所实现的是 Linux 和 Linux 之间的共…

新手搭建知识付费平台必备攻略:如何以低成本实现高转化?

我有才知识付费平台 一、引言 随着知识经济的崛起&#xff0c;越来越多的知识提供者希望搭建自己的知识付费平台。然而&#xff0c;对于新手来说&#xff0c;如何以低成本、高效率地实现这一目标&#xff0c;同时满足自身需求并提高客户转化率&#xff0c;是一大挑战。本文将…

SPA, SEO, SSR总结

SPA单页面Web应用 SPA(Single page web application) 单页面Web应用 Web不再是一张张页面,而是一个整体的应用,一个由路由系统,数据系统,页面(组件)系统等等,组成的应用程序, 让用户不需要每次与服务器进行页面刷新来获得新的内容, 从而提供了更快,跟流畅的用户体验, 在SPA中…

参与创作①周年啦~

写在前面 今天看了消息才知道&#xff0c;原来开始创作已经一年了。此篇无干货&#xff0c;纯白话&#xff0c;纯记录。 机缘 参与CSDN创作已经一年有余&#xff0c;犹记得第一篇博文是为了整理好所学内容&#xff0c;方便自己复习。没想到后面也陆陆续续发了些其他内容&…

关于read函数阻塞的问题

关于read函数阻塞的问题 上一篇文章IO多路转接之select 末尾提到了一点&#xff0c;服务端读取每次是读取10个字节的&#xff0c;如果超过10个字节&#xff0c;需要读取多次&#xff0c;但是客户端只会read一次&#xff0c;第二次read的时候&#xff0c;直接阻塞了。 那么如何…

Windows server flask

1、Windows server 通过python的flask执行命令 from flask import Flask, request, abort import subprocess from flask_basicauth import BasicAuth app Flask(__name__) # 获取url是进行账号密码认证&#xff0c;设置url的账号密码 app.config[BASIC_AUTH_USERNAME] 账号…

12.8作业

1.头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QMovie>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nul…

spring-boot-starter-validation是什么Validation参数校验使用概要

spring-boot-starter-validation是什么&Validation参数校验使用概要 来源Valid和Validated的用法(区别)引入依赖Valid和Validated的用法 在日常的项目开发中&#xff0c;为了防止非法参数对业务造成的影响&#xff0c;需要对接口的参数做合法性校验&#xff0c;例如在创建用…

基于Docker安装Mysql:5.5

一、拉取镜像 sudo docker pull mysql:5.5二、启动mysql镜像 1. 创建MySQL的conf目录和data目录 mkdir -p /home/docker/mysql/conf /home/docker/mysql/data2. 利用镜像创建容器 sudo docker run --restartalways -d --name mysql -v /home/docker/mysql/conf/my.cnf:/etc…

系统设计-微服务架构

典型的微服务架构图 下图展示了一个典型的微服务架构。 负载均衡器&#xff1a;它将传入流量分配到多个后端服务。CDN&#xff08;内容交付网络&#xff09;&#xff1a;CDN 是一组地理上分布的服务器&#xff0c;用于保存静态内容以实现更快的交付。客户端首先在 CDN 中查找内…

methods

类型&#xff1a;{ [key: string]: Function } 详细&#xff1a; methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法&#xff0c;或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。 注意&#xff0c;不应该使用箭头函数来定义 method 函数 (例如…

临床骨科常用的肩关节疾病量表,医生必备!

根据骨科医生的量表使用情况&#xff0c;常笑医学整理了临床骨科常用的肩关节疾病量表&#xff0c;为大家分享临床常见的肩关节疾病量表评估内容&#xff0c;均支持量表下载和在线使用&#xff0c;建议收藏&#xff01; 1.臂、肩、手功能障碍&#xff08;disabilites of the ar…

useradd 在Linux原生应用开发过程中的简单应用

useradd命令是用于在Linux系统中创建新用户的命令。它可以创建一个新用户&#xff0c;并设置该用户的属性、家目录、默认shell等。useradd命令实际上是一个包装了一系列系统调用的高级命令。 在Linux系统中&#xff0c;用户信息存储在/etc/passwd文件中。当执行useradd命令时&…

flstudio21破解汉化版2024最新水果编曲使用教程

​ 如果你一直梦想制作自己的音乐(无论是作为一名制作人还是艺术家)&#xff0c;你可能会想你出生在这个时代是你的幸运星。这个水果圈工作室和上一版之间的改进水平确实令人钦佩。这仅仅是FL Studio 21所提供的皮毛。你的音乐项目的选择真的会让你大吃一惊。你以前从未有过这…

ChatGPT的常识

什么是ChatGPT&#xff1f; ChatGPT是一个基于GPT模型的聊天机器人&#xff0c;GPT即“Generative Pre-training Transformer”&#xff0c;是一种预训练的语言模型。ChatGPT使用GPT-2和GPT-3两种模型来生成自然语言响应&#xff0c;从而与人类进行真实的对话。 ChatGPT的设计…

2023年全球软件开发大会(QCon广州站2023)-核心PPT资料下载

一、峰会简介 本次峰会包含&#xff1a;泛娱乐时代的边缘计算与通讯、稳定性即生命线、下一代软件架构、出海的思考、现代数据架构、AGI 与 AIGC 落地、大前端技术探索、编程语言实战、DevOps vs 平台工程、新型数据库、AIGC 浪潮下的企业出海、AIGC 浪潮下的效能智能化、数据…

【池式组件】线程池的原理与实现

线程池的原理与实现 线程池简介1.线程池1.线程池2.数量固定的原因3.线程数量如何确定4.为什么需要线程池5.线程池结构 线程池的实现数据结构设计1.任务结构2.任务队列结构3.线程池结构 接口设计 线程池的应用reactorredis 中线程池skynet 中线程池 线程池简介 1.线程池 1.线程…

第7课 SQL入门之创建计算字段

文章目录 7.1 计算字段7.2 拼接字段使用别名 7.3 执行算术计算 这一课介绍什么是计算字段&#xff0c;如何创建计算字段&#xff0c;以及如何从应用程序中使用别名引用它们。 7.1 计算字段 存储在数据库表中的数据一般不是应用程序所需要的格式&#xff0c;下面举几个例子。 …

前端Excel导出实用方案(完整源码,可直接应用)

目录 前言&#xff1a; 技术选型&#xff1a; 主要功能点&#xff1a; 核心代码&#xff1a; 完整代码&#xff1a; 开发文档 前言&#xff1a; 在前后端分离开发为主流的时代&#xff0c;很多时候&#xff0c;excel导出已不再由后端主导&#xff0c;而是把导出的操作移…