linux文件——重定向原理——dup、重定向与execl、VFS
前言:本篇讲解linux下的重定向相关内容。 在本篇中, 博主将会带着友友们一边实验, 一边探索底层原理。 通过本篇的学习, 友友们将会了解到重定向是如何实现的, 重定向的本质是什么, 重定向和进程替换之间的关系等等, 本篇内容将会丰富我们对于进程的理解。
ps:由于本节内容涉及到文件fd,所以本节内容适合了解文件fd的友友们进行观看。
目录
文件描述符的分配规则
重定向的本质
dup
dup2的使用——输出重定向
dup2的使用——输入重定向
自定义shell实现重定向指令
文件的重定向和进程替换
重定向的参数
如何理解计算机下“一切皆文件”
文件描述符的分配规则
想要知道文件描述符的分配规则, 我们需要使用一个实验来测试出来。 下面我们开始进行这个实验:
在这个实验中, 我们会用到wrrite, open函数, 如下图为man手册:
其中, open函数需要包含头文件sys/types.h、sys/stat.h、fcntl.h
write函数需要包含unistd.h头文件
然后, 我们的代码如下:
需要用到的头文件:
下面是我们的代码:
这个程序运行后, 就是如下结果:
图中打印fd, 然后将hello linux的内容打印到log.txt文件中, 再输出log.txt的内容, 就如同上图
上图的fd打印为3, 我们知道, 0, 1, 2对应的是stdin, stdout, stderr。所以新文件fd就到了3号fd。
接下来, 就开始测试文件描述符的分配规则。
从0号小标开始, 寻找最小的没有使用的数组位置,它的下标就是新文件的文件描述符。
我们从上图可以看出, 文件描述符是3。 而0, 1, 2都被占用了。 我们就可以考虑——对于文件来说, 文件描述符都是从小到大创建的。
那么我们为了验证这个猜想, 就可以消除0号下标的指向。 那么0号就空出来了。 这个时候我们再创建的文件就是被映射在了0号下标处。
下面是测试代码:
然后打印出来的fd如下:
上面的结果就是说, 消除了0号fd位置的指针, 当我们再打开一个文件的时候就可以将这个文件指针放入0号位置。
我们再关闭1号fd进行测试:
然后运行结果:
![]()
没有打印出内容的原因是因为1号是显示器文件, 关闭后就不会再向显示器中打印了。
关闭2再测试一下
然后运行结果:
清除2号指针后, 然后打开文件, 2号就会保存新打开的文件指针。 然后打开的fd就变成了2, 打印出来的是2, 同样符合我们的假设。
- 那么现在就可以下结论了——文件描述符对应的分配规则是什么? 从0下标开始, 寻找最小的没有使用的数组位置, 它的下标新文件的文件描述符。
重定向的本质
我们在上面探究文件描述符的分配规则的时候, 知道了1号文件描述符被清空后, 再新打开的文件的文件指针就会保存到1号文件描述符中。 ——这个过程起始就是重定向。
下面重新捋一下这个过程, 对于上面这个过程, 我们的进程本来有一个文件描述符表:

然后我们将1号fd指向显示器的文件的指针收回, 然后创建新文件log.txt, 将log.txt的struct_files的地址放到1号文件的fd处。

我们看下面的具体代码:
上面这个1号fd转化到过程, 也就是上面黄框框的代码段。 对于操作系统来说, 他知不知道fd的指向发生了变化呢? 答案是不知道!!对于操作系统来说, 他不管fd下面做了什么, 他只认fd。 所以, 如果还向1里面写东西, 那么就是本来向显示器文件里面写东西转化为向log.txt文件里面写东西。 而这个过程就是重定向。
那么我们如果想要向其他文件里面写东西, 是不是就需要将这个文件的指针覆盖到1号fd里面? ——这就是重定向。 重定向只需要将想要重定向到文件的指针覆盖到1号所在的fd里面!!
dup
上面我们讲道理重定向的底层原理。 但是整个代码很长——需要一开始关闭1号fd文件描述符指针, 然后将新打开文件的文件描述符指针放到1号文件中。 实际上, 系统就是提供了一种fd覆盖的接口——dup系列, 下面是man手册:
上面有三个dup系列函数, 常用的是dup2. 下面我们具体查看一下dup2的用法:
第一个参数名为newfd, 对应上面的1号fd, 第二个参数名是oldfd, 对应上面的新打开的文件。 也就是说将oldfd里面的内容拷贝到newfd里面。
dup2的使用——输出重定向
dup2可以直接将数组中的一个fd覆盖到另一个数组fd。 我们dup2的第一个参数是新打开的文件fd, 第二个参数是要拷贝到的fd的位置。
如下为代码:
运行结果如下:
我们也可以把清空写改成追加写:
运行结果如下:
dup2的使用——输入重定向

先创建一个数组进行拷贝拷贝, 然后向显示器中读取, 如果读取, 那么打印读取的内容。
此时是向键盘中读取:

我们使用dup2, 将新打开的文件覆盖到0号fd。 就是输入重定向, 将新打开的文件的数据打印:
如图就是将新打开文件的数据打印到inbuffer。 再将inbuffer的数据打印。 我们在log.txt里面写上aaaaaaaa

下面是打印内容:

自定义shell实现重定向指令
如何自己实现>, >>, < 指令
要自己实现>, >>, <指令, 我们就要拿出我们之前写的自定义shell的代码了。
在代码中, 我们需要先新定义几个宏——NONE代表没有重定向, IN_RDIR代表输入重定向, OUT_RDIR代表输入输出重定向, APPEND_RDIR代表追加重定向。
也要定义两个新的变量——rdirfilename指向重定向文件的首地址, rdir代表重定向的标志。
如下图宏定义:
新创建的变量:
在交互函数里面分析是否有重定向, check_rdir就是重定向判断的函数:
下图是check_rdir的实现:
然后我们再在执行普通命令的板块里面创建一个新的代码块。 也就是当id == 0的时候, 判断此时的rdir的状态, 如果是NONE才是exec, 正常加载执行逻辑。 如下是代码:
然后我们还要在每次输入指令的时候都给rdir和rdirfilename做初始化:

运行出结果之后:

文件的重定向和进程替换
现在有一个问题, 就是在重定向的时候, 我们修改了fd。 然后加载了子进程, 为什么这样做是正确的呢?——要解决这个问题, 就要拿起进程的知识了, 如下图:

在上面的图里面, PCB和文件管理, 是内核数据结构; 而虚拟地址空间, 物理内存, 页表, 是进程数据结构, 这两个是结偶关系。 而对于物理内存, 程序和代码加载替换掉物理内存, 页表重新映射物理内存。 这个过程, 在内核数据结构里, 并不关心。

所以, 文件的重定向和进程替换之间互不影响!!!
重定向的参数
我们使用重定向, 可能遇到下图这种只有一部分数据重定向到了新文件, 但是还有一部分直接打印到了显示器的情况:


上面描述的问题就是重定向了一部分, 但是还有一部分没有重定向, 这是因为对于重定向来说, 默认是将打印到显示器的数据重定向到文件中。
- 想要将两部分——stdout、stderr两个部分的数据都进行重定向, 就需要使用参数fd, 使用方式如下: fd > 文件。
如下图使用:
这两种方法我们要谈的是第二种方法:./newfile.exe 1 > both.log 2>&1, 这里面2>&1的意思就是说, 将1fd的内容拷贝到2fd里面去。 而1已经重定向到了both.log, 所以, 将1的内容拷贝到2fd里面去后。 本应该打印到2fd里面的内容也会被打印到文件中。
如何理解计算机下“一切皆文件”
对于计算机来说, 所有的操作计算机的动作, 都是以进程的方式进行操作的。所有访问文件的操作, 最终都是用进程的方式访问文件的。
计算机上所有应用的所有操作, 最终都会被系统解释成进程。 目前, 所有对文件的操作, 全部都依赖进程的操作。
而且, 我们知道, 对于冯诺依曼体系结构来说, 底层大部分都是外设!!!如下图:
上面就是一个一个打开文件后创建的结构体, 下面就是底层硬件。
- 对于上面图中的底层设备, 每一个外设的读写方法都是不一样的, 也就是他们的struct file是不一样的。 所以这个时候每一个struct file里面都有一个指针指向struct operation_func类型的结构体。
如下图:
那么, 未来操作系统为了进行文件操作, 就会先创建一个进程:
然后, 操作系统又专门给我们定义了系统调用:

所以, 操作系统就实现了在上层统一使用read, write接口, 然后在下层根据文件的不同, 找到不同的write, read方法。
所以, 一切皆文件——就是操作系统在文件层面上封装一层struct file结构体对象, 然后, 根据这个对象里面的指针找到对应文件的write, read。 而这里的write, read同样是一层封装各种各样读写方法的结构体。 而真正的各种设备的读写方法如何实现, 我们并不关心!!!
从struct file往上, 就是用户!是给我们看到的, 我们看到的, 就是struct file。 看到的就是——一切皆文件!!!
所以, 在linux中, 在struct file这一层, 被称作VFS——virtual file system虚拟文件系统。
- 以后, 当我们的进程再想实现open, write这些接口的时候, 就会先找到struct file。 然后struct file就回去找到自己里面的operation_func, 至于operation里面是什么情况, struct file并不关心。 而这, 就是多态。 这里面的一层一层的指针的包含关系, 就叫做继承!!
以上, 就是本节的全部内容, 下面是博主整理的个人笔记:





相关文章:
linux文件——重定向原理——dup、重定向与execl、VFS
前言:本篇讲解linux下的重定向相关内容。 在本篇中, 博主将会带着友友们一边实验, 一边探索底层原理。 通过本篇的学习, 友友们将会了解到重定向是如何实现的, 重定向的本质是什么, 重定向和进程替换之间的…...
【STM32 FreeRTOS】任务
使用 RTOS 的实时应用程序可以被构建为一组独立的任务。每个任务在自己的上下文中执行,不依赖于系统内的其他任务或 RTOS 调度器本身。在任何时间点,应用程序中只能执行一个任务,实时 RTOS 调度器负责决定所要执行的任务。因此, R…...
Java面试--框架--Spring MVC
Spring MVC 目录 Spring MVC1.spring mvc简介2.spring mvc实现原理2.1核心组件2.2工作流程 3.RESTful 风格4.Cookie,Session4.1 会话4.2 保存会话的两种技术 5.拦截器5.1过滤器、监听器、拦截器的对比5.2 过滤器的实现5.3 拦截器基本概念5.4 拦截器的实现 1.spring …...
土壤水分监测系统的工作原理
TH-TS200土壤水分监测系统是一种在地球科学、农学等领域广泛应用的分析仪器,它主要用于监测土壤中的水分含量,为农业生产、水资源管理、环境保护等提供重要数据支持。通常包括数据采集器、土壤水分传感器、土壤温度传感器(部分系统配备)、计算机软件以及…...
k8s学习--如何控制pod调度的位置
文章目录 一、Pod 调度基础二、通过节点选择器 (Node Selector) 控制调度三、使用节点亲和性 (Node Affinity)四、使用污点和容忍 (Taints and Tolerations)五、Pod 反亲和性 (Pod Anti-Affinity) 总结 在 Kubernetes (K8s)中,Pod 是应用运行的最小单位࿰…...
基于mysqldump的MySQL数据库异地备份方案(含完整脚本和解释)
MySQL数据库异地备份方案 0 文档描述 本文描述了一个数据库异地备份方案,以下脚本代码都是在线上应用的本文以CentOS7为例,其他系统请自行查询安装命令如果评论有需求,我就对应系统做一下文档 1 基本原理 1.1 流程 原理本身很简单&#…...
C语言中10个字符串函数详解
目录 1.strlen 2.strcpy 3.strcat 4.strcmp 5.strncpy 6.strncat 7.strncmp 8.strstr 9.strtok 10.strerror 1.strlen 基本结构:size_t strlen(const char *str);功能:用于计算字符串的长度;字符串已经 0作为结束标志…...
flume系列之:查询多个flume agent组是否有topic重复接入情况
flume系列之:查询多个flume agent组是否有topic重复接入情况 一、查询zk节点下的flume agent组二、获取采集的topic三、获取重复接入的topic,支持设置重复接入白名单四、执行流程五、完整代码一、查询zk节点下的flume agent组 def get_flumeAgent_zkPath(zkRootPaths):for z…...
Windows自动化1️⃣环境搭建WinAppDriver
对于技术选型: 我尝试了, pywinauto, WinAppDriver,CukeTest 担心CukeTest可能会收费, 尝试pywinauto,在元素点击,搜索时, 遇到不可用情况; WinAppDriver是微软家的,大厂开源, 就它了! 步骤一:安装WinAppDriver 进入WinAppDriver下载页面(https://githu…...
云服务器Docker内部署服务后,端口无法访问?
云服务器Docker内部署服务后,端口无法访问,可以按照以下思路进行排查: 以【docker run --name my-nginx -d -p 9395:80 nginx】举例: 查看Docker映射是否正确,可使用docker ps命令查看。Docker是否设置端口映射&#…...
Unity将摄像机视角保存成Json文件方便读取使用
系列文章目录 unity工具 文章目录 系列文章目录👉前言👉一、设置环境👉二、代码如下👉三、使用方法 👉四、下次外部调用json里面的摄像机位置的时候如下代码方法👉壁纸分享👉总结 👉…...
git是什么/基本指令
git作用 去中心化, 分布式版本控制器 新增术语:仓库区, 工作区, 暂存区 具体见下板书 常用git命令 git clone 仓库网址 git status 查看仓库状态 git add newfile 临时添加到git仓库 git commit -m 正式添加git仓库 g…...
Linux 中的同步机制
代码基于:Kernel 6.6 临界资源:指哪些在同一时刻只允许被一个线程访问的软件或硬件资源。这种资源的特点是,如果有线程正在使用,其他进程必须等待直到该线程释放资源。 临界区:指在每个线程中访问临界资源的那段代码。…...
Day17 枚举、typedef、位运算、堆空间的学习
目录 枚举 typedef 位运算 堆上的空间 枚举 一个一个列举出来,是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。 作用: 1、为了提高代码的可读性 2、提高代码的安全性 枚举类型 基本语法: enum 枚举名 { …...
Python爬虫与数据分析:中国大学排名的深度挖掘
前言 👉 小编已经为大家准备好了完整的代码和完整的Python学习资料,朋友们如果需要可以扫描下方CSDN官方认证二维码或者点击链接免费领取【保证100%免费】 一、选题背景 高考作为中国学生生涯中最为重要的事,在高考之后,选择一所…...
微软开源库 Detours 详细介绍与使用实例分享
目录 1、Detours概述 2、Detours功能特性 3、Detours工作原理 4、Detours应用场景 5、Detours兼容性 6、Detours具体使用方法 7、Detours使用实例 - 使用Detours拦截系统库中的UnhandledExceptionFilter接口,实现对程序异常的拦截 C软件异常排查从入门到精通…...
js中的getElementById的使用方法
在JavaScript中,document.getElementById()是一种用于通过元素的id属性获取DOM元素的方法。它的作用是返回与指定id匹配的HTML元素。 使用document.getElementById()可以通过元素的id属性直接获取该元素的引用,然后可以使用该引用对元素进行各种操作。例…...
设计模式 - 桥接模式
💝💝💝首先,欢迎各位来到我的博客!本文深入理解设计模式原理、应用技巧、强调实战操作,提供代码示例和解决方案,适合有一定编程基础并希望提升设计能力的开发者,帮助读者快速掌握并灵活运用设计模式。 💝💝💝如有需要请大家订阅我的专栏【设计模式】哟!我会定…...
LeetCode530 二叉搜索树的最小绝对差
前言 题目: 530. 二叉搜索树的最小绝对差 文档: 代码随想录——二叉搜索树的最小绝对差 编程语言: C 解题状态: 成功解决! 思路 注意题目中的二叉搜索树,这个条件暗示每个节点的左子节点肯定小于该节点&am…...
【STM32 FreeRTOS】信号量与互斥锁
二值信号量 二值信号量的本质是一个队列长度为1的队列,该队列就只有空和满两种情况,这就是二值。 二值信号量通常用于互斥访问或任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题,所以二值…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
初探用uniapp写微信小程序遇到的问题及解决(vue3+ts)
零、关于开发思路 (一)拿到工作任务,先理清楚需求 1.逻辑部分 不放过原型里说的每一句话,有疑惑的部分该问产品/测试/之前的开发就问 2.页面部分(含国际化) 整体看过需要开发页面的原型后,分类一下哪些组件/样式可以复用,直接提取出来使用 (时间充分的前提下,不…...
用 FFmpeg 实现 RTMP 推流直播
RTMP(Real-Time Messaging Protocol) 是直播行业中常用的传输协议。 一般来说,直播服务商会给你: ✅ 一个 RTMP 推流地址(你推视频上去) ✅ 一个 HLS 或 FLV 拉流地址(观众观看用)…...
联邦学习带宽资源分配
带宽资源分配是指在网络中如何合理分配有限的带宽资源,以满足各个通信任务和用户的需求,尤其是在多用户共享带宽的情况下,如何确保各个设备或用户的通信需求得到高效且公平的满足。带宽是网络中的一个重要资源,通常指的是单位时间…...
Spring AI中使用ChatMemory实现会话记忆功能
文章目录 1、需求2、ChatMemory中消息的存储位置3、实现步骤1、引入依赖2、配置Spring AI3、配置chatmemory4、java层传递conversaionId 4、验证5、完整代码6、参考文档 1、需求 我们知道大型语言模型 (LLM) 是无状态的,这就意味着他们不会保…...


























