【Linux学习笔记】系统文件IO之重定向原理分析
【Linux学习笔记】系统文件IO之重定向原理分析
🔥个人主页:大白的编程日记
🔥专栏:Linux学习笔记
文章目录
- 【Linux学习笔记】系统文件IO之重定向原理分析
- 前言
- 一. 系统文件I/0
- 1.1 一种传递标志位的方法
- 1.2 hello.c写文件:
- 1.3 hello.c读文件
- 1.4 接口介绍
- 1.5 open函数返回值
- 1.6 文件描述符fd
- 1.6.1 0&1&2
- 1.6.2 文件描述符的分配规则
- 1.6.3 重定向
- 1.6.4 使用dup2系统调用
- 1.6.5 在minishell中添加重定向功能
- 后言
前言
哈喽,各位小伙伴大家好!上期我们讲了进程替换和自定义shell 今天我们讲的是系统文件IO之重定向原理分析。话不多说,我们进入正题!向大厂冲锋!
一. 系统文件I/0
打开文件的方式不仅仅是fopen,ifstream等流式,语言层的方案,其实系统才是打开文件最底层的方案。不过,在学习系统文件IO之前,先要了解下如何给函数传递标志位,该方法在系统文件IO接口中会使用到:
1.1 一种传递标志位的方法
#include <stdio.h>#define ONE
#define TWO
0001 //0000 00010002 //0000 0010#define THREE 0004 //0000 0100void func(int flags) {if (flags & ONE) printf("flags has ONE! ");if (flags & TWO) printf("flags has TWO! ");if (flags & THREE) printf("flags has THREE! ");printf("\n");}int main() {func(ONE);func(THREE);func(ONE | TWO);func(ONE | THREE | TWO);return 0;}
操作文件,除了上小节的C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来]进行文件访问,先来直接以系统代码的形式,实现和上面一模一样的代码:
1.2 hello.c写文件:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{umask(0);int fd = open("myfile", O_WRONLY | O_CREAT, 0644);if (fd < 0) {perror("open");return 1;}int count = 5;const char* msg = "hello bit!\n";int len = strlen(msg);while (count--) {}write(fd, msg, len);//fd: 后⾯讲,msg:缓冲区⾸地址,len :本次读取,期望写⼊多少个字节的数据。//返回值:实际写了多少字节数据close(fd);return 0;
}
1.3 hello.c读文件
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <string.h>int main(){int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}const char *msg = "hello bit!\n";char buf[1024];while(1){ssize_t s = read(fd, buf, strlen(msg));//类⽐write if(s > 0){printf("%s", buf);}else{break;}}close(fd);return 0;}
1.4 接口介绍
open man open
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);pathname:
要打开或创建的⽬标⽂件flags:
打开⽂件时,可以传⼊多个参数选项,⽤下⾯的⼀个或者多个常量进⾏“或”运算,构成flags
。
参数
:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定⼀个且只能指定⼀个
O_CREAT : 若⽂件不存在,则创建它。需要使⽤mode选项,来指明新⽂件的访问权限
O_APPEND: 追加写
返回值:
成功:新打开的⽂件描述符
失败:-1
mode_t理解:直接man
手册,比什么都清楚。
open
函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open
创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open
。
write read closelseek
,类比c文件相关接口。
1.5 open函数返回值
在认识返回值之前,先来认识一下两个概念:系统调用和库函数
- 上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。
- 而 open close read write lseek 都属于系统提供的接口,称之为系统调用接口
- 回忆一下我们讲操作系统概念时,画的一张图
系统调用接口和库函数的关系,一目了然。
所以,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。
1.6 文件描述符fd
- 通过对open函数的学习,我们知道了文件描述符就是一个小整数
1.6.1 0&1&2
- Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0,标准输出1,标准错误2.
- 0,1,2对应的物理设备一般是:键盘,显示器,显示器
所以输入输出还可以采用如下方式:
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>int main(){char buf[1024];ssize_t s = read(0, buf, sizeof(buf));if(s > 0){buf[s] = 0;write(1, buf, strlen(buf));write(2, buf, strlen(buf));}return 0;}
而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files,指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件
描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。
1.6.2 文件描述符的分配规则
直接看代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(){int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);}close(fd);return 0;
}
输出发现是fd:3
关闭0或者2,在看
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(){close(0);//close(2);int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;}
发现是结果是:fd:0或者fd2,可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
1.6.3 重定向
那如果关闭1呢?看代码:
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdlib.h>int main(){close(1);int fd = open("myfile", O_WRONLY|O_CREAT, 00644);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);fflush(stdout);close(fd);exit(0);}
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件myfile当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>,>>,<
那重定向的本质是什么呢?
1.6.4 使用dup2系统调用
函数原型如下:
#include <unistd.h>int dup2(int oldfd, int newfd);
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>int main() {int fd = open("./log", O_CREAT | O_RDWR);if (fd < 0) {perror("open");return 1;}close(1);dup2(fd, 1);for (;;) {char buf[1024] = {0};ssize_t read_size = read(0, buf, sizeof(buf) - 1);if (read_size < 0) {perror("read");break;}printf("%s", buf);fflush(stdout);}return 0;
}
printf是C库当中的IO函数,一般往stdout中输出,但是stdout底层访问文件的时候,找的还是fd:1,但此时,fd:1下标所表示内容,已经变成了myfifile的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向。那追加和输入重定向如何完成呢?
1.6.5 在minishell中添加重定向功能
先定义一个整数记录重定向方式 一个字符串记录重定向文件
TrimSpace跳过重定向字符后的空格 指向文件名开始位置
每次调用RedirCheck先清空文件和让文件描述符为0
然后从后向前查找> >> <
每次让命令行参数表清空重定及其之后的内容 设置为\0即可 命令正常执行
然后根据找到的符号 调用跳过空格函数 在让文件名指向end之后的内容
设置重定向方式即可
Execute根据redir 调用open打开文件 然后dup2重定向即可
之后再让子进程正常执行即可
- 问题:进程替换会不会影响重定向结果?
不会 因为进程替换只是替换程序的代码和数据
而文件结构体和文件描述符等内核数据结构不受到影响。
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
int redir = NONE_REDIR;
std::string filename;
void RedirCheck(char cmd[])
{redir = NONE_REDIR;filename.clear();int end = strlen(cmd)-1;while (end > 0){if (cmd[end] == '>'){if (cmd[end - 1] == '>'){cmd[end - 1] = 0;redir = APPEND_REDIR;}else{cmd[end] = 0;redir =OUTPUT_REDIR;}TrimSpace(cmd, ++end);filename = cmd + end;break;}else if (cmd[end] == '<'){cmd[end] = 0;redir = INPUT_REDIR;TrimSpace(cmd, ++end);filename = cmd + end;break;}else{end--;}}
}
int Execute()
{pid_t id = fork();if (id == 0){int fd = -1;// 子进程检测重定向情况if (redir == INPUT_REDIR){fd = open(filename.c_str(), O_RDONLY);if (fd < 0) exit(1);dup2(fd, 0);close(fd);}else if (redir == OUTPUT_REDIR){fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);if (fd < 0) exit(2);dup2(fd, 1);close(fd);}else if (redir == APPEND_REDIR){fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);if (fd < 0) exit(2);dup2(fd, 1);close(fd);}execvp(g_argv[0], g_argv);exit(1);}int status = 0;pid_t rid=waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);}return 0;
}
int main()
{//初始化环境变量表InitEnv()**加粗样式**;while (1){//打印命令行提示符PrintCommandPrompt();//获取命令行输入char commandline[COMMAND_SIZE];if (!GetCommandLine(commandline, sizeof(commandline))){continue;}RedirCheck(commandline);cout << redir << "->" << filename << endl;//填充命令行参数表if (!CommandParse(commandline)){continue;}//处理内建命令if (CheckAndExecBuiltion()){continue;}//执行命令Execute();}return 0;
}
后言
这就是系统文件IO之重定向原理分析。大家自己好好消化!今天就分享到这! 感谢各位的耐心垂阅!咱们下期见!拜拜~
相关文章:

【Linux学习笔记】系统文件IO之重定向原理分析
【Linux学习笔记】系统文件IO之重定向原理分析 🔥个人主页:大白的编程日记 🔥专栏:Linux学习笔记 文章目录 【Linux学习笔记】系统文件IO之重定向原理分析前言一. 系统文件I/01.1 一种传递标志位的方法1.2 hello.c写文件:1.3 he…...
【新教程】Linux服务器ssh启用两步验证
1 背景 服务器被恶意破解的事件层出不穷,一旦被破解就比较麻烦。不如提前通过简单的措施——增加两步验证,来大大增强服务器的安全性。本教程在Debian 12.5、Ubuntu 24.04等系统上测试通过。 2 详细过程 1、安装 libpam-google-authenticator sudo a…...

SpringBoot中使用MCP和通义千问来处理和分析数据-连接本地数据库并生成实体类
文章目录 前言一、正文1.1 项目结构1.2 项目环境1.3 完整代码1.3.1 spring-mcp-demo的pom文件1.3.2 generate-code-server的pom文件1.3.3 ChatClientConfig1.3.4 FileTemplateConfig1.3.5 ServiceProviderConfig1.3.6 GenerateCodeController1.3.7 Columns1.3.8 Tables1.3.9 Fi…...

实现滑动选择器从离散型的数组中选择
1.使用原生的input 详细代码如下: <template><div class"slider-container"><!-- 滑动条 --><inputtype"range"v-model.number"sliderIndex":min"0":max"customValues.length - 1"step&qu…...

基于Credit的流量控制
流量控制(Flow Control),也叫流控,它是控制组件之间发送和接收信息的过程。在总线中,流控的基本单位称为flit。 在标准同步接口中(比如AXI协议接口),握手信号如果直接采用寄存器打拍的方式容易导致信号在不同的方向上出现偏离。因…...

【金仓数据库征文】金仓数据库KingbaseES: 技术优势与实践指南(包含安装)
目录 前言 引言 一 : 关于KingbaseES,他有那些优势呢? 核心特性 典型应用场景 政务信息化 金融核心系统: 能源通信行业: 企业级信息系统: 二: 下载安装KingbaseES 三:目录一览表: 四:常用SQL语句 创建表: 修改表结构…...
LLaVA:开源多模态大语言模型深度解析
一、基本介绍 1.1 项目背景与定位 LLaVA(Large Language and Vision Assistant)是由Haotian Liu等人开发的开源多模态大语言模型,旨在实现GPT-4级别的视觉-语言交互能力。该项目通过视觉指令微调技术,将预训练的视觉编码器与语言模型深度融合,在多个多模态基准测试中达到…...

金丝猴食品:智能中枢AI-COP构建全链路数智化运营体系
“金丝猴奶糖”,这个曾藏在无数人童年口袋里的甜蜜符号,如今正经历一场数智焕新。当传统糖果遇上数字浪潮,这家承载着几代人味蕾记忆的企业,选择以数智化协同运营平台为“新配方”,将童年味道酿成智慧管理的醇香——让…...
泛型设计模式实践
学海无涯,志当存远。燃心砺志,奋进不辍。 愿诸君得此鸡汤,如沐春风,事业有成。 若觉此言甚善,烦请赐赞一枚,共励学途,同铸辉煌! 为解决在设计框架或库时遇到的类型安全问题ÿ…...

java的输入输出模板(ACM模式)
文章目录 1、前置准备2、普通输入输出API①、输入API②、输出API 3、快速输入输出API①、BufferedReader②、BufferedWriter 案例题目描述代码 面试有时候要acm模式,刷惯leetcode可能会手生不会acm模式,该文直接通过几个题来熟悉java的输入输出模板&…...

鸿蒙 所有API缩略图鉴
从HarmonyOS NEXT Developer Preview1(API 11)版本开始,HarmonyOS SDK以 Kit 维度提供丰富、完备的开放能力,涵盖应用框架、应用服务、系统、媒体、AI、图形在内的六大领域,共计30000个API...
【LangChain全景指南】构建下一代AI应用的开发框架
目录 🌟 前言🏗️ 技术背景与价值🚧 当前技术痛点🛠️ 解决方案概述👥 目标读者说明 🔍 一、技术原理剖析📊 核心概念图解💡 核心作用讲解🧩 关键技术模块说明⚖️ 技术选…...
垃圾对象回收
1.如何判断对象可以被回收 对象是否可以被回收通常由垃圾回收器决定。 垃圾回收器使用一种称为"可达性分析"的算法来确定对象是否可被回收。 可达性分析是指如果一个对象无法从任何GCRoots直接或间接访问到,它就被认为是不可达的,可以被垃圾回…...

【Docker系列】使用格式化输出与排序技巧
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
nvme Unable to change power state from D3cold to D0, device inaccessible
有个thinkpad l15 gen4笔记本,使用较少,有一块三星m2和东芝14t硬盘,想安装飞牛nas系统作为家庭照片库,制作飞牛启动盘,发现安装飞牛需要全盘格式化,电脑本身的系统还是需要保留的,故想到再安装一…...

基础语法(二)
Mysql基础语法(二) Mysql基础语法(二)主要介绍Mysql中稍微进阶一点的内容,会稍微有一些难度(博主个人认为)。学习完基础语法(一)和基础语法(二)之…...
AcWing 877:扩展欧几里得算法
【题目来源】 https://www.acwing.com/problem/content/879/ 【题目描述】 给定 n 对正整数 ai,bi,对于每对数,求出一组 xi,yi,使其满足 aixibiyigcd(ai,bi)。 【输入格式】 第一行包含整数 n。接下来 n 行,每行包含两个整数 ai…...
WebRTC流媒体传输协议RTP点到点传输协议介绍,WebRTC为什么使用RTP协议传输音视频流?
通过上一章《WebRTC工作原理详细介绍、WebRTC信令交互过程和WebRTC流媒体传输协议介绍》,我们知道WEBRTC在完成 SDP 协商和 ICE 候选交换信令后,双方就可以建立 RTP 流,开始传输音视频数据,这时,RTP 数据包就通过在 IC…...

TOA的定位,建模与解算的步骤、公式推导
TOA(到达时间)定位的核心是通过测量信号从目标到多个基站的传播时间,将其转换为距离信息,并利用几何关系解算目标位置。本文给出具体的建模与解算步骤及公式推导 文章目录 通用模型建立非线性方程组构建线性化处理(最小二乘法)最大似然估计(ML)高斯-牛顿迭代法误差分析…...
Python序列化的学习笔记
1. Npy&Numpy O4-mini-Cursor:如果.npy文件里包含了「Python对象」而非纯数值数组时,就必须在加载时加上allow_pickleTrue。...
[C++] 大数减/除法
目录 高精度博客 - 前两讲高精度减法高精度除法高精度系列函数完整版 高精度博客 - 前两讲 讲次名称链接高精加法[C] 高精度加法(作用 模板 例题)高精乘法[C] 高精度乘法 高精度减法 void subBIG(int x[], int y[], int z[]){z[0] max(x[0], y[0]);for(int i 1; i < …...

2025年PMP 学习七 -第5章 项目范围管理 (5.4,5.5,5.6 )
2025年PMP 学习七 -第5章 项目范围管理 5.4 创建 WBS 1.定义与作用 定义把项目可交付成果和项目工作分解成较小的,更易于管理的组件作用对所要交付的内容提供一个结构化的视图 2.输入,输出,工具与技术 3. 创建WBS的依据(输入&…...

CAD属性图框值与Excel联动(CAD块属性导出Excel、excel更新CAD块属性)——CAD c#二次开发
CAD插件实现块属性值与excel的互动,效果如下: 加载dll插件(CAD 命令行输入netload ,运行xx即可导出Excel,运行xx1即可根据excel更新dwg块属性值。) 部分代码如下 // 4. 开启事务更新CAD数据using (Transact…...

【HarmonyOS 5】鸿蒙中进度条的使用详解
【HarmonyOS 5】鸿蒙中进度条的使用详解 一、HarmonyOS中Progress进度条的类型 HarmonyOS的ArkUI框架为开发者提供了多种类型的进度条,每种类型都有其独特的样式,以满足不同的设计需求。以下是几种常见的进度条类型: 线性进度条(…...
Vue3响应式原理源码解析(通俗易懂版)
一、Vue3响应式核心流程 reactive(): 通过Proxy代理目标对象拦截get/set/deleteProperty等操作使用Reflect执行默认行为 依赖收集: get时通过track函数收集依赖(当前执行的effect)使用WeakMap建立"target -> key -> d…...
milvus+flask山寨复刻《从零构建向量数据库》第7章
常规练手,图片搜索山寨版。拜读罗云大佬著作,结果只有操作层的东西可以上上手。 书中是自己写的向量数据库,这边直接用python拼个现成的milvus向量数据库。 1. 创建一个向量数据库以及对应的相应数据表: # Milvus Setup Argume…...

Spring Cloud: Nacos
Nacos Nacos是阿里巴巴开源的一个服务发现,配置管理和服务管理平台。只要用于分布式系统中的微服务注册,发现和配置管理,nacos是一个注册中心的组件 官方仓库:https://nacos.io/ Nacos的下载 Releases alibaba/nacos 在官网中…...
AI生成视频推荐
以下是一些好用的 AI 生成视频工具: 国内工具 可灵 :支持文本生成视频、图片生成视频,适用于广告、电影剪辑和短视频制作,能在 30 秒内生成 6 秒的高清视频(1440p),目前处于免费测试阶段。 即…...

Win11安装APK方法详解
1、官方win11系统 预览版 开发版 正式版 都行 2、同时你还需要开启主板 BIOS 虚拟化选项(具体名称不同主板略有不同) 这一步自行百度 开始:先去确定有没有开启虚拟化 任务管理器检查—— 虚拟化是否已经开启,如果没有自己去BIO…...

SSH终端登录与网络共享
SSH 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议 注意 SSH终端登录的前提是:电脑和板卡都能够通过网络相连接及通信 与连接互联网不一样,SSH可以不用互联网,只要电脑和板卡组成一个小型网络即可 网络方案 如果您…...