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的队列,该队列就只有空和满两种情况,这就是二值。 二值信号量通常用于互斥访问或任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题,所以二值…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

华为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…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...