[Linux] 逐层深入理解文件系统 (2)—— 文件重定向

news2024/10/23 23:30:27

标题:[Linux] 逐层深入理解文件系统 (2)—— 文件重定向

个人主页@水墨不写bug

(图片来源于网络)

目录

一、文件的读取和写入

 二、文件重定向的本质

1.手动模拟重定向的过程——把标准输出重定向到redir.txt

2.重定向函数dup2

3.命令行的重定向指令操作 

四、缓冲区的刷新策略


正文开始:

一、文件的读取和写入

        在深入理解文件系统(1)中,我们深入了解了文件的打开与关闭:操作系统通过文件描述符fd来对文件进行管理操作。此外,我们知道了文件的系统调用级别的打开方式,但是我们还没有了解如何读写文件的内容。

文件的写入同样有对应的系统调用:

 参数列表:

        fd:想要写入的文件的对应的文件描述符(对打开文件后会得到对应的文件描述符)

        *buf:这是一个我们定义的数组,是一个缓冲区。

        count:写入的字节数

返回值:

        成功,返回写入的字节数;失败,返回-1,并设置错误码。

文件的读取的系统调用:

参数列表:

        fd:想要读取的文件的对应的文件描述符

        *buf:把文件的内容读取到这个数组中。

        count:读取的字节数。

返回值:

        成功,返回读取的字节数;失败,返回-1,设置错误码。

 二、文件重定向的本质

        文件重定向的本质是在内核中改变文件描述符表的特定下标,和上层的语言层面的无关。

1.手动模拟重定向的过程——把标准输出重定向到redir.txt

        1)使用系统调用close关闭  标准输出(fd==1的文件)

        2)使用系统调用open打开redir.txt文件

        这个时候向标准输出打印信息就会被重定向到redir.txt文件中。

底层原理:

        首先我们关闭了标准输出(文件fd==1的文件),文件描述符标的下标1的这位置就被空出来了,由于文件描述符的分配规则是从小到大来分配的,当我们打开一个新的文件(redir.txt)这个文件的fd就会被分配为1。

        我们向标准输出写入,本质是向文件描述符表下标1的位置对应的内核级缓冲区写入,由于redir.txt分配到了1这个位置,所以向原来的标准输出的缓冲区写入,就是向redir.txt的缓冲区写入。

        这样就实现了文件的重定向。

 关闭标准输出后打开redir.txt:


2.重定向函数dup2

函数原型 :

简单总结:

        函数作用:让newfd对应的内容成为oldfd对应的内容的拷贝,本质是文件描述符下标所对应的内容的拷贝,并在结束的时候首先关闭newfd。

注意:

        1)oldfd不是一个有效的fd,则调用失败, newfd不会被关闭。

        2)oldfd是有效的fd,但是newfd和oldfd对应同一个文件,dup2函数不做任何事。

         dup2系统调用函数的意义在于可以让我们的重定向操作更加方便比如如果我们还是想要把标准输出重定向到redir.txt文件,那么只需要调用一个系统调用函数即可:

int fd1 = open("redir.txt",O_CREAT | O_WDONLY);

dup2(fd1,1);//这样就实现了把标准输出重定向到fd1

3.命令行的重定向指令操作 

       我们写出这样的一个代码:

#include<stdio.h>
int main()
{
    fprintf(stdout,"hello stdout\n");
    fprintf(stderr,"hello stderr\n");
    
    return 0;
}

        编译完运行:

         发现标准输出和标准错误都是显示器,这符合预期。


        对一个项目,我们可以把运行结果重定向到不同的日志文件中,方便后续维护:

        上面的操作是把运行结果(向显示器输出重定向到两个文件中):

        标准输出重定向到ok.txt;

        标准错误重定向到err.txt。 

       


如果不加fd直接重定向,会仅仅把标准输出重定向:

         标准错误仍被打印到显示器。

       


如果想把标准输出和标准错误重定向到一个文件,需要:


四、缓冲区的刷新策略

        在之前,我们知道在文件IO中 缓冲区存在两个,一个是C语言层面的缓冲区,一个是系统内核级缓冲区,但是我们并不了解缓冲区究竟是什么。

        是什么:缓冲区就是一段连续的内存空间。

        为什么:将系统调用和与硬件交互解耦,将C语言函数调用与系统调用解耦;提高刷新效率,从而在整体上提高IO效率,为用户提供高效的IO体验。

        怎么办:这就需要谈到不同的刷新策略问题了。

        对于不同的文件,缓冲区的刷新策略不同。常见刷新策略有如下几种:

                1)立刻刷新。比如调用fflush(stdout)(立刻刷新语言缓冲区)   ,   fsync(fd)(立刻刷新系统缓冲区)等。

                2)行刷新。显示器,因为显示器需要照顾用户查看习惯。

                3)  全缓冲,缓冲区写满才刷新。比如普通文件。

此外,对于特殊情况也会进行缓冲区刷新:

                a)进程退出,系统自动刷新缓冲区

在了解了缓冲区刷新策略之后,我们看看下面这样的场景:

#include<stdio.h>
#include<string.h>
#include<unistd.h>

int main()
{
    
    printf("hello printf\n");
    fprintf(stdout,"hello fprintf\n");

    const char* msg = "hello write\n";
    write(1,msg,strlen(msg));

    return 0;
}

         编译成功运行结果:

当我们重定向到log.txt,并打印出来:

         发现顺序不一样了,原因在于缓冲区向文件写入时刷新策略发生了变化:

        对一次是向显示器写入,刷新策略是按行刷新,由于三次打印有带有换行符,所以是按照代码的顺序打印输出。

        第二次是向文件写入,策略是全缓冲,所以尽管带有换行符,前两次写入都是暂时写入到了C语言级别的缓冲区内了,没有立刻被刷新到内核级缓冲区内。但是write是系统调用,直接刷新到了内核级缓冲区,所以最先被写入文件,后来在进程结束前,语言级缓冲区刷新到内核级,再被刷新到磁盘的文件中。

 这个场景还有一个变式:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>

int main()
{
    printf("hello printf\n");
    fprintf(stdout,"hello fprintf\n");

    const char* msg = "hello write\n";
    write(1,msg,strlen(msg));

    fork();
    return 0;
}

运行后直接输出到显示器:

        在意料之内。

但是重定向到log.txt文件并cat打印出来:

        发现了不对劲,其实造成这样的现象本质也是缓冲区刷新策略的变化导致的。

        write是系统调用,会直接刷新到内核级缓冲区。但是这个时候前两个语句已经被执行了,但是数据还存在于语言缓冲区内。这个时候fork创建了一个子进程,子进程的数据和父进程相同,在进程结束之前,两个进程都刷新了自己的语言级缓冲区,导致前两个打印语句被执行了两次! 


完~

未经作者同意禁止转载

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

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

相关文章

支付宝开放平台-开发者社区——AI 日报「10 月 14 日」

1 大模型「强崩溃」&#xff01;Meta新作&#xff1a;合成数据有「剧毒」&#xff0c;1%即成LLM杀手 新智元&#xff5c;阅读原文 1%合成数据&#xff0c;就能让模型瞬间崩溃&#xff01;甚至&#xff0c;参数规模越大&#xff0c;模型崩溃越严重。Nature封面一篇论文证实&am…

Acwing 前缀与差分

1.一维前缀和 一维前缀和&#xff1a;S[i]a1a2a3a4…ai&#xff0c;要求a从a1开始&#xff0c;且S[0]0 前缀和的作用&#xff1a;给定一组序列数据&#xff0c;可以计算任意第l个数到第r个数的和&#xff0c;S[r]-S[l-1]&#xff08;这里就解释了为什么要求S[0]0&#xff0c;因…

电力电子技术(一)

变压器漏感对整流电路的影响&#xff1a;

Find My微型电磨机|苹果Find My技术与电磨机结合,智能防丢,全球定位

微型电磨机是一种多功能电动工具&#xff0c;主要用于打磨、抛光、雕刻、钻孔等多种作业。‌ 它由控制箱和电磨笔两部分组成&#xff0c;通过直流稳压电源供电&#xff0c;电磨笔以直流马达为驱动源&#xff0c;带动磨头进行高速旋转机械运动&#xff0c;配合不同材质、形状的磨…

制药企业MES与TMS的数据库改造如何兼顾安全与效率双提升

*本图由AI生成 在全球制造业加速数字化转型的浪潮中&#xff0c;一家来自中国的、年营业额超过200亿元的制药企业以其前瞻性的视角和果断的行动&#xff0c;成为该行业里进行国产化改造的先锋。通过实施数据库改造试点项目&#xff0c;该企业实现了其关键业务系统MES&#xff0…

请求的响应----状态码分为五大类(爬虫)

前言 一个爬虫的成功与否&#xff0c;在于你是否拿到了想要的数据&#xff1b;一个请求的成功与否&#xff0c;在于响应的状态码&#xff0c;它标明了当前请求下这个响应的结果&#xff0c;是好还是坏。上节课程学习了HTTPS和HTTP协议的各自优势&#xff0c;本节课程进入到请求…

C++: AVL树的实现

一.AVL树的旋转 AVL树是平衡搜索二叉树的一种。 平衡因子&#xff1a;节点右树的高度减左树的高度&#xff0c;AVL树规定平衡因子的绝对值小于2。若不在这个范围内&#xff0c;说明该树不平衡。 AVL树节点&#xff1a; struct AVLTreeNode {AVLTreeNode(const T& data …

【AI 新观察】“转人工!转人工!”——智能客服痛点与破局之路

在当今数字化时代&#xff0c;智能客服在电商等众多领域被广泛应用&#xff0c;然而&#xff0c;一句又一句“转人工&#xff01;转人工&#xff01;”却常常暴露出智能客服存在的痛点。一、智能客服之痛 1. 理解偏差引不满 智能客服在理解客户问题时&#xff0c;常常出现偏差…

代码随想录 -- 回溯 -- 解数独

37. 解数独 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; class Solution(object):def back(self,board):for i in range(len(board)):for j in range(len(board[0])):if board[i][j] ! .:continuefor k in range(1,10):if self.isValid(i,j,k,board):board[i][j…

为什么要做自动化测试

一、自动化测试 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。 个人认为&#xff0c;只要能服务于测试工作&#xff0c;能够帮助我们提升工作效率的&#xff0c;不管是所谓的自动化工具&#xff0c;还是简单的SQL 脚本、批处理脚本&#xff0c;还是自己编写…

10.13论文阅读

通过联合学习检测和描述关键点增强可变形局部特征 摘要 局部特征提取是计算机视觉中处理图像匹配和检索等关键任务的常用方法。大多数方法的核心理念是图像经历仿射变换&#xff0c;忽略了诸如非刚性形变等更复杂的效果。此外&#xff0c;针对非刚性对应的新兴工作仍然依赖于…

2024 年江苏省职业院校技能大赛“区块链技术应用” 赛项赛卷(样卷)运维题解析二

运维题 环境: ubuntu20 fisco 2.8.0 前言 准备两台机子,并且可以能相互pin通 192.168.19.133 [M1-A] 192.168.19.137 [M2-B] 子任务 1-2-3:区块链节点运维 基于已搭建的区块链系统与控制台,在机器(M1-A)上开展区块链群组与 节点的运维工作,具体内容如下: (1)基于…

Jenkins如何更改主目录文件夹?设置路径?

Jenkins如何更改主目录文件夹&#xff1f;设置路径&#xff1f;?简单几步&#xff0c;让你轻松解决。 工具/原料 联网电脑 方法/步骤 1. ssh连接到jenkins的服务器。使用root权限执行下面的命令&#xff0c;第一句是查看jenkins是否启用&#xff0c;启用的话需要第二个命令关…

肽合同制造(CDMO):北美和欧洲是全球最大肽合同制造(CDMO)消费地区

据 HengCe 最新调研&#xff0c;2023年中国肽合同制造&#xff08;CDMO&#xff09;市场销售收入达到了 万元&#xff0c;预计2030年可以达到 万元&#xff0c;2024-2030期间年复合增长率(CAGR)为 %。本研究项目旨在梳理肽合同制造&#xff08;CDMO&#xff09;领域产品系列&am…

【linux开发-驱动】-linux内核相关

开发板&#xff1a;STM32MP157 一、编译linux内核源码 编译完成以后就会在 arch/arm/boot 这个目录下生成一个叫做 uImage 的文件&#xff0c;uImage 就是 我们要用的 Linux 镜像文件。 Linux 编 译 的 时 候 需 要 设 置 目 标 板 架 构 ARCH 和 交 叉 编 译 器 CROSS_COMP…

确认:代码覆盖率是无用的管理指标

发现拆解代码覆盖率指标的简单证明 代码覆盖率是衡量软件产品质量的一个强有力的指标&#xff0c;多年来&#xff0c;技术领导者们对此深信不疑。从表面上看&#xff0c;其理由似乎很充分&#xff1a;测试越彻底&#xff0c;代码覆盖率就越高&#xff0c;因此&#xff0c;我们…

数据屏蔽与加密:代理用户需要了解的内容

您可能已经意识到数据安全和隐私的重要性。尽管存在各种方法来解决这两个问题&#xff1a;道德考量和监管要求&#xff0c;在本指南中&#xff0c;我们将重点介绍两种流行的策略&#xff1a;屏蔽和加密 - 以及它们的比较。 那么&#xff0c;哪个方法更胜一筹呢&#xff1f;答案…

时间序列预测(三)——激活函数(Activation Function)

激活函数是神经网络中每个神经元的输出函数&#xff0c;用于引入非线性&#xff0c;从而使神经网络能够逼近复杂的非线性关系。没有激活函数的网络只能表示线性变换&#xff08;如上一篇的线性回归不需要激活函数&#xff09;&#xff0c;因此不能解决实际中的非线性问题。激活…

【Python】从零到一,搭建高效Web服务器,轻松上手!Python开发者必备(文末附带源码分享)

CSDN Python源码分享&#xff1a;实现一个简单的Web服务器 在CSDN上&#xff0c;我们经常分享各种技术文章和源码&#xff0c;帮助开发者们不断提升自己的技能。今天&#xff0c;我将为大家分享一个使用Python实现的简单Web服务器源码。这个Web服务器能够处理基本的HTTP GET请…

Java项目实战II基于Java+Spring Boot+MySQL的桂林旅游景点导游平台(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 桂林&#xff0c;以其独特的喀斯特地貌、秀美的自然风光闻名遐迩&#xff0c;每年吸引着无数国内外游…