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的队列,该队列就只有空和满两种情况,这就是二值。 二值信号量通常用于互斥访问或任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题,所以二值…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...

【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...