【Linux修行路】基础I/O——重定向的实现原理

news2024/9/21 14:47:25

目录

⛳️推荐

一、再来理解重定向

1.1 输出重定向效果演示

1.2 重定向的原理

1.3 dup2

1.4 输入重定向效果演示

1.5 输入重定向代码实现

二、再来理解标准输出和标准错误

2.1 同时对标准输出和标准错误进行重定向

2.2 将标准输出和标准错误重定向到同一个文件

三、再看一切皆文件


⛳️推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站

一、再来理解重定向

1.1 输出重定向效果演示

分析ls 指令是显示当前目录下的文件,本质就是将当前目录下所有的文件名以字符串的形式写入到显示器文件。采用输出重定向 >,将原本应该写入显示器文件的内容写入到了 log.txtx 文件中。

1.2 重定向的原理

在讲解重定向原理前,我们需要明确文件描述符的分配规则,即从0下标开始,寻找最小的没有使用的数组位置,它的下标就是新打开文件的文件描述符。这里没有使用的意思是该下标里面存的是 NULL,即没有指向任何一个文件对象。下面通过一段代码来为大家展示重定向的原理。

// mytest.c
int main()
{
    close(1);
    int fd = open(FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if(fd < 0)
    {
        perror("open");
        return errno;
    }

    const char* str = "Hello Linux!\n";
    int cnt = 5;
    while(cnt--)
    {
        write(1, str, strlen(str));
    }
    return 0;
}

代码分析:上面这段代码就完美的展示了重定向的原理。首先调用 close 系统调用将 1 号下标对应的文件关闭,关闭的意思就是将 1 下标里的内容置为 NULL,原本 1 下标里面存储的内容是显示器文件对象的地址,也就是标准输出 stdout,紧接着调用 open 打开了一个文件,根据文件描述符的分配规则,新打开的这个文件的文件描述符就是 1,即文件描述符表(file*的数组)1 号下标里面存储的就是新打开的文件对象的地址。接下来调用 write 接口,向 1 号文件描述符中进行写入,本来 1 号文件描述符对应的是显示器文件,原本向显示器文件中写入的内容,此时就被写入到新打开的文件中,没有向显示器文件中写入,因此屏幕上就不会出现字符串,至此整个重定向的过程就结束啦。

总结:重定向的本质是对数组下标里面的内容进行修改。

1.3 dup2

上面介绍了重定向的原理,下面介绍一下实现重定向的系统调用 dup2

#include <unistd.h>
int dup2(int oldfd, int newfd);

dup2 的具体实现并不是向上面代码中那样,先将一个文件描述符关闭,然后紧接着再打开一个文件。dup2 的使用方法是,用户在调用 dup2 接口前,正常打开一个文件,不用将显示器文件关闭,此时新打开文件的文件描述符就是 3。接下来调用 dup2 ,将新打开文件的文件描述符作为 oldfd,将显示器文件的文件描述符也就是 1,作为 newfd。我们知道,文件描述符本质上就是数组下标,dup2 函数中执行的工作就是将 oldfd 下标里存储的文件对象地址拷贝到 newfd 下标里面,至此重定向工作就完成了。

小Tipsdup2 的函数形参有一个误导,我们可能会觉得新打开文件的描述符是 newfd,其实不然,这里的 newfd 是将要被覆盖的文件描述符,oldfd 是新打开文件的描述符。

代码分析:上面就是输出重定向的实现原理,追加重定向只需要把 O_TRUNC 替换成 O_APPEND

1.4 输入重定向效果演示

分析cat 指令本来是从键盘文件中获取输入然后写入显示器文件中,采用输入重定向 < 后,是从 log.txt 文件中获取输入然后写入显示器文件中。

1.5 输入重定向代码实现

// 输入重定向
int main()
{
    int fd = open(FILE_PATH, O_RDONLY);
    if(fd < 0)
    {
        perror("open");
    }
    dup2(fd, 0);

    char str[1024];
    ssize_t ret = read(fd, str, sizeof(str) - 1);
    if(ret > 0)
    {
        str[ret] = '\0';
        printf("echo: %s", str);
    }
    return 0;
}

小Tips:进程历史打开的文件与进行的各种重定向关系都和未来进行的程序替换无关,程序替换并不影响文件访问。进程打开文件和何种重定向工作,本质上都是进程管理的模块,而程序替换只会把用户空间的代码和数据完全被新程序替换,不会影响到进程管理。

二、再来理解标准输出和标准错误

int main()
{
    fprintf(stdout, "Standard output messages\n");
    fprintf(stdout, "Standard output messages\n");
    fprintf(stdout, "Standard output messages\n");

    fprintf(stderr, "Standard error messages\n");
    fprintf(stderr, "Standard error messages\n");
    fprintf(stderr, "Standard error messages\n");
    return 0;
}

代码分析> 是输出重定向,也就是对标准输出(1号文件描述符)进行重定向。标准错误对应的2号文件描述符并没有进行重定向,因此标准错误消息仍然打印在了屏幕上。

2.1 同时对标准输出和标准错误进行重定向

./mytest 1>output.txt 2>error.txt

小Tips:这段代码就是将1号文件描述符对应的标准输出文件重定向到 output.txt 文件,将2号文件描述符对应的标准错误文件重定向到 error.txt 文件。这样以来屏幕上就不会有任何输出。

2.2 将标准输出和标准错误重定向到同一个文件

./mytest 1>all.txt 2>&1

小Tips:将标准输出和标准错误都重定向到 all.txt 文件中。

三、再看一切皆文件

所有操作计算机的动作,都是通过进程去执行的,所有的访问文件操作,都是通过进程去实现的,目前所有对文件的操作都依赖于进程。

小Tips:所有的外设都被抽象成了文件,每个外设都有自己的读写方法,不同的外设读写方法一定是不同的。但是我们在对文件进行读写操作的时候,始终调用的都是 read 和 write 方法,这是因为操作系统为我们提供了一个方法集类型 file_operations,该结构体里面都是函数指针类型,指向外设的各种方法,这就是多态的雏形。所谓的一切皆文件,就是操作系统帮我们封装了一层文件对象,进程对各种外设的操作,全都变成了对文件的操作。

sszie_t read(int fd)
{
	task_struct->files->fd_array[fd]->f_op->read();
}

🎁结语:

        今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是我前进的动力!

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

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

相关文章

版本更新 《坚持学习计时器》软件V3.1 更新内容:自动实时显出

&#x1f31f; 嗨&#xff0c;我是命运之光&#xff01; &#x1f30d; 2024&#xff0c;每日百字&#xff0c;记录时光&#xff0c;感谢有你一路同行。 &#x1f680; 携手启航&#xff0c;探索未知&#xff0c;激发潜能&#xff0c;每一步都意义非凡。 版本更新 《坚持学习…

【统计字符数量】统计出每种字符的数量

输入一行字符&#xff0c;分别统计出其中英文字母、空格、数字和其他字符的个数&#xff0c;使用C语言实现&#xff0c; 具体代码&#xff1a; #include<stdio.h>int main(){char c;int letters0,space0,digit0,others0;printf("请输入一行字符&#xff1a; "…

SpringBoot整合Junit单元测试(解决空指针异常)

1.依赖 依赖只需要导入Spring-Boot-starter、Spring-Boot-test&#xff08;不需要另导入junit依赖&#xff09; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test…

Docker的安装和基本用法

&#x1f4a5; 该系列属于【SpringBoot基础】专栏&#xff0c;如您需查看其他SpringBoot相关文章&#xff0c;请您点击左边的连接 目录 一、在linux虚拟机上安装Docker 1. 卸载旧版本Docker 2. 配置Docker的yum库 3. 安装Docker 4. 启动和校验 二、Docker基本用法 1. Do…

Unity项目优化记录

背景&#xff1a;测试反馈项目组游戏存在内存泄露&#xff0c;来找到中台这边协调排查。好家伙&#xff0c;跑了两次看了内存快照&#xff0c;再看资源组织和管理方式&#xff0c;存在的问题确实比较多。 1、修复内存泄露&#xff1a;结算界面由于资源引用丢失导致整个面板不会…

无人机水面影像几何校正拼接镶嵌

一批水面数据3千张&#xff0c;原图如下所示&#xff1a; 使用“无人机水面影像拼接算法”进行几何校正和匀色镶嵌处理&#xff0c;输出结果如下所示&#xff1a; 镶嵌一张图&#xff1a; 局部放大图&#xff1a; 数据代处理联系方式&#xff1a;

重定向printf、或者USARTX_printf()(hal库)

目录 重定向printf USARTX_printf() 打开工程模板 复制工程模板 配置 重定向printf 在usart.h添加 #include "stdio.h" 在usart.c添加 /* USER CODE BEGIN 1 */ int fputc(int ch,FILE *f) {HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY)…

使用html-docx-js + fileSaver实现前端导出word

因为html-docx-js是16年的老库了&#xff0c;它代码里面用到的with语法现在严格模式不允许&#xff0c;用npm直接引入会报错&#xff0c;所以我们需要用其它方式引入 首先要将html-docx-js的代码放到项目中 html-docx-js/dist/html-docx.js at master evidenceprime/html-do…

java 日期

Date SimpleDateFormat Date d new Date();System.out.println(d);//输出当前时间long it d.getTime();//返回自1970年1月1日00:00:00到date的时间Date d2 new Date(it);//将毫秒值转换为时间System.out.println(d2);d2.setTime(it1000);//将d2转换为输入参数对应的时间Sys…

vue 后台管理 之 状态管理 vuex 的使用

幸福是一种能力 文章目录 一、数据驱动视图二、VueX 数据公共池 一、数据驱动视图 我们都知道 vue 之所以好用&#xff0c;是因为官方帮我们做了数据驱动视图初始化时将数据和视图进行绑定&#xff0c;通过 watcher 来监听数据的变化&#xff0c;当数据变化时&#xff0c;会触…

数据结构(邓俊辉)学习笔记】优先级队列 07——堆排序

1.算法 作为完全二叉堆的一个应用&#xff0c;这节来介绍堆排序算法。 是的&#xff0c;谈到优先级队列&#xff0c;我们很自然地就会联想到排序。因为就其功能而言&#xff0c;包括完全二叉堆在内的任何一种优先级队列都天生地具有选取功能&#xff0c;也就是选取其中的最大…

【微服务】SpringCloud 1-9章

1 从Boot和Cloud版本选型开始说起 1.1 Springboot版本选择 1.1.1 git源码地址 https://github.com/spring-projects/spring-boot/releases/ 1.1.2 官网看Boot版本 1.1.3 SpringBoot3.0崛起 https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-N…

SpringSecurity实现登录功能实战!!!

实现思路 登录 ①自定义登录接口 调用ProviderManager的方法进行认证 如果认证通过生成jwt 把用户信息存入redis中 ②自定义UserDetailsService 在这个实现类中去查询数据库 注意配置passwordEncoder为BCryptPasswordEncoder 退出登录&#xff0c;删除redis中的数…

vuex 辅助函数用法:mapGetters,mapMutations,mapActions

1 mapGetters:用法 <template> //普通用法 <p> {{ $store.getters.getCounter }} </p> //辅助函数用法 <p> {{ getCounter }} </p> </template> import { mapGetters } from vuex export default{ name:"homeView&q…

损失函数-回归模型|分类模型

损失函数 Loss Function 回归模型均方误差&#xff08;MSE: Mean Squared Error&#xff09;均方根误差&#xff08;RMSE&#xff1a;Root Mean Squared Error&#xff09;平均绝对误差&#xff08;MAE: Mean Absolute Error&#xff09;Huber损失 分类模型交叉熵损失&#xff…

MT6705 同步整流

MT6705B 是用于反激式变换器的高性能45V同步整流。MT6705B兼容各种反激转换器类型。支持 DCM、CCM 和准谐振模式。MT6705集成了一个40V功率MOSFET&#xff0c;可以取代肖特基二极管&#xff0c;提高效率。V SW <V TH-ON 时&#xff0c;内部 MOSFET 导通。V SW >V TH-OFF …

Qt/C++地图标注点的添加删除移动旋转/指定不同图标和动图/拿到单击信号

一、前言说明 标注点在地图开发中是最常见的应用场景之一&#xff0c;比如在地图上需要显示设备的位置&#xff0c;基本上都是添加标注点&#xff0c;指定图片和尺寸已经经纬度坐标位置。这个功能在每种地图内核中都提供的&#xff0c;这个并没有任何难点&#xff0c;在这个功…

类和对象 - 构造函数

文章目录 一、语言的向前兼容二、默认成员函数三、构造函数3.1 概念3.2 自己定义的构造函数3.2.1 有参和无参的构造函数3.2.1 有缺省参数的构造函数 3.3 默认构造函数3.3.1 几种默认构造函数3.3.2 默认构造函数的特点 3.4 编译器生成的默认构造函数3.4.1 函数初始化规则 四、构…

Android:动态更新app启动图标和应用名

一、需求背景 每逢重要佳节&#xff0c;很多应用启动图标会自动更新为对应佳节的图标&#xff0c;应用无需更新。 二、效果图 更新后的启动图标和应用名称 三、实现流程 Android app只能替换内置的icon&#xff0c;因此需要提前将logo图标放入App资源文件件里 实际项目App更新…

vue 路由用法 router-view

通过router-view 点击子路由显示子路由关于我们的内容&#xff0c;点击关于信息显示关于信息内容。