文件系统 --- 重定向,缓冲区

news2025/1/16 0:55:02

序言

 本篇文章的内容和上一篇文章 👉点击查看 紧密相连,所以为了更好的理解本篇文章,需要大家将前置知识准备好哦😇。
 本文主要向大家介绍文件的重定向,以及基于用户级别的缓冲区和基于操作系统级别的缓冲区。原来看似简单的文件操作背后居然有这么多东西。

1. 重定向

1.1 引出问题

 在上一篇文章的结束,我们提出了序号为 1 的文件描述符指向的是 显示器文件,之后我们执行了下段代码:

 13 int main(){
 14 
 15     umask(0);
 16 
 17     close(1);// 关闭显示器文件
 18 
 19     // 写方式打开文件,并且文件不存在就创建一个,文件的权限是 rw-rw-r--
 20     int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
 21     if(fd == -1) perror("open");
 22     printf("fd = %d.\n", fd);        
 23                                                                                                                                                                                                               
 24                                                          
 25     return 0;                         
 26 }

执行结果并没有在显示器上打印相应的内容,而是如下:

  • 增添了一个名为 log.txt 文件
  • 文件里面的内容就是需要打印的内容

非常奇怪,我们打印的内容怎么会跑到文件中呢?

1.2 思考推理

 我们尝试顺着代码的逻辑进行推理💡,一切都是有迹可循的!!!
 首先,close(1);:这段代码关闭了显示器文件,大家还记得这幅图吗:

在这里插入图片描述
这张图大致的描述了系统如何管理的文件,以及进程通过进程描述符表控制文件。现在我们使用 close(1) 操作,这将清除描述符表中下标为 1 的内容:
在这里插入图片描述
咦?你只是将下标为 1 的位置置空了呀,并没有真正的释放该文件在内存中的空间呀?好的,知识加餐了哦☀️:


知识加餐:智能指针技术

 我们知道 1 号文件对应的是显示器文件,请问在你的系统中,只有现在你控制的进程才会使用显示器文件吗?当然不是,其他有些进程可能也在使用,只是你不知道!在系统中也是如此,可能多个进程控制同一个文件,所以你不用不代表其他进程不使用!
 在上一篇文章中,我们介绍了文件结构体 struct file 里包含的信息,其中就包括:

f_count:表示有多少进程或文件描述符引用了这个文件。当 f_count 降到 0 时,文件将被关闭。

这其实就是一种智能指针技术,确保了资源(动态分配的内存)的自动且安全的释放,避免了内存泄漏,同时也避免了在对象生命周期结束之前过早地删除对象(即“悬挂指针”或“野指针”问题)


 言归正传,所以现在下标为 1 的位置就空了,之后我们使用了 open() 函数创建了一个新文件,我们是不是需要将该文件的 struct file 指针放入我们的文件描述符表呀,然后操作系统从上到下遍历,发现 1 的位置是空的,代表没有存储任何其他文件结构体指针,那行,就放在这里吧:
在这里插入图片描述
现在 1 下标对应的文件就是 log.txt 了哈。

  最后我们使用了 printf 函数,现在你只需要记住,后面我们会细讲,这个函数很老实,只会向下标为 1 的文件写入内容,(我们平时往显示器打印内容,说到底就是向显示器文件写入内容呀),当下标为 1 指向的文件改变了,不关我的事,我只负责写,不负责你是谁!

 破案了,这就是底层发生的事!本来写给显示内容,由于显示器文件被释放了,又创建了个文件占据了原来显示器文件所处于进程描述符表中的位置,并且 printf
也只会将内容输入到 下标为 1 的文件中,导致输入到新创建的文件中。

 你说了这么多,口说无凭呀!证明一下呢!其实我们只需要运行程序,查看生成的文件内容就好啦,为什么呢?因为:printf("fd = %d.\n", fd);,我们文件打印的内容本身就是该文件的 fd,如果该文件的 fd 确实等于 1,那么就没问题:
在这里插入图片描述
😄Bingo !!!

1.3 什么是重定向

 我们不是应该介绍重定向吗?怎么上面花了大量篇幅说明其他事情呢?其实这就是重定向!准确的说,应该是 输出重定向

 文件重定向允许用户改变程序的 标准输入(stdin)、标准输出(stdout)或标准错误(stderr)默认流向 。默认情况下,标准输入来自键盘,标准输出和标准错误输出到屏幕(控制台)。通过文件重定向,用户可以 将这些默认流向更改为文件或其他程序,从而实现更灵活的数据输入和输出处理。

 我们将本该输入到屏幕的内容,通过重定向,将改内容输入到了指定文件当中。

1.4 重定向函数 dup2

 现在大家应该大致知道什么是文件重定向了,但是不是稍显麻烦了呀,每次我们都需要去关闭指定的文件。

 你能够想到的,你的操作系统也肯定想到啦!而且已经为你准备了对应的系统调调用接口函数:

int dup2(int oldfd, int newfd);
  • oldfd:待复制的文件描述符,它必须是一个已经打开的文件描述符。
  • newfd:目标文件描述符,即 dup2 函数会将 oldfd 复制到的文件描述符。
  • 返回值:成功时,返回新的文件描述符(即 newfd ),失败返回 -1

就比如我想要将本该输入到 1 对应文件的内容输入到 4 对应的文件中,就应该是:

dup2(4, 1);

举个栗子:

 13 int main(){
 14 
 15     umask(0);
 16 
 17     // 写方式打开文件,并且文件不存在就创建一个,文件的权限是 rw-rw-r--
 18     int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
 19     if(fd == -1) perror("open");
 20 
 21     dup2(fd, 1);                                                                                                                                                                                              
 22 
 23     printf("fd = %d.\n", fd);
 24 
 25 
 26     return 0;
 27 }

2. FILE

 在使用系统调用接口 open 打开 / 创建 一个文件时,会返回文件描述符 fd。而我们使用 C语言提供给我们的编程接口 fopen 打开 / 创建一个文件时,会返回一个指针 FILE*

2.1 FILE 结构体

FILE 其实就是 C语言封装的结构体,我们可以推断出该结构体里面肯定包含有 fd。因为 C语言的封装是基于系统调用接口的,所以你不能脱离我本来就有的内容,可以比我多出一部分内容,不可能少!

 现在我们来介绍该结构体中比较重要的包含的信息:

  • 文件状态标志(File Status Flags):这些标志用于指示文件流的状态.。
  • 文件位置指示器(File Position Indicator):这个指示器跟踪当前在文件中的位置,通常是一个表示字节偏移量的整数或指针。
  • 缓冲区(Buffer):为了提高 I/O 操作的效率。
  • 文件描述符(File Descriptor):该文件描述符是内核用来标识打开文件的。

2.2 stdin、stdout、stderr

 在上一篇文章中,我们提到进程在启动时会为我们自动打开三个文件(分别关于 标准输入,标准输出,标准错误输出),并分配文件描述符 0, 1, 2 。他们到了 C语言中被封装成了一个 FILE结构体:

  • 文件描述符 0:对应于 stdin(标准输入)。默认情况下,它指向用户的键盘输入,但可以通过重定向将其指向文件或其他输入源。
  • 文件描述符 1:对应于 stdout(标准输出)。默认情况下,它用于向终端屏幕输出数据,但同样可以通过重定向将其输出到文件或其他输出设备。
  • 文件描述符 2:对应于 stderr(标准错误输出)。它通常用于输出错误信息,以便即使 stdout 被重定向,错误信息仍然能够显示在终端屏幕上。然而,stderr 也可以被重定向。

printf 是一个 C语言函数,在上面我们说到他只会向 文件标识符为 1 的文件写入内容,其实这是有点不准确的,在 C语言中,就应该按照他的方式,所以应该说是 printf 函数只会向 stdout 文件流写入内容。

2.3 fprintf 函数介绍

fprintf 比起 printf 灵活些,它可以将格式化的数据写入到 指定 的输出流中:

int fprintf(FILE *stream, const char *format, ...);
  • FILE *stream:指向 FILE 对象的指针,该 FILE 对象标识了要写入数据的输出流。
  • const char *format:一个格式化字符串,用于指定后续参数如何被格式化和写入输出流。
  • 返回值:fprintf 函数返回写入的字符数,如果发生错误,则返回一个负值。

举个栗子:

 13 int main(){
 14 
 15     umask(0);
 16 
 17     // 写方式打开文件,并且文件不存在就创建一个,文件的权限是 rw-rw-r--
 18     int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
 19     if(fd == -1) perror("open");
 20 
 21 
 22     fprintf(stdout, "fd = %d.\n", fd);                                                                                                                                                                        
 23 
 24 
 25     return 0;
 26 }

3. 缓冲区

3.1 内核级别的缓冲区

 在上一篇文章中👉点击查看 我们介绍了内核级别的缓冲区,该缓冲区被操作系统所控制,主要的功能是减少对硬件的直接访问次数,从而提高系统的整体性能。 在这里就不多赘述了。🐷

3.2 用户级别的缓冲区

 大家清细看 FILE 结构体里面包含什么,是不是有一个 缓冲区(Buffer),这就是用户级别的缓冲区,该缓冲区被进程所控制。

 那为什么要大费周章的再弄一个用户级别的缓冲区呢?就拿些内容举例:我要将我的内容写到一个文件中,最终是不是还是要先写到系统控制的缓冲区当中呢?肯定是的呀,所以要调用系统调用的接口将内容写到缓冲区当中。但是调用系统调用接口也是会有代价的,它涉及到用户态到内核态的切换以及上下文的保存和恢复。通过使用用户级别的缓冲区,程序可以在一次系统调用中传输更多的数据,从而减少系统调用的次数,提高程序的执行效率。

3.3 缓冲区的刷新机制

 缓冲区的刷新机制是内存管理中一个重要的概念,将介绍何时缓冲区会进行一次刷新:

  • 无缓冲(Unbuffered)
    数据一写入到缓冲区就立即刷新到磁盘或其他存储介质。
    这种策略适用于需要立即处理数据的情况,但可能会降低性能,因为每次写入都会触发系统调用。

  • 行缓冲(Line Buffered)
    当在缓冲区中遇到换行符(\n)时,缓冲区中的数据会被刷新到磁盘或其他存储介质。
    显示器(stdout)通常采用行缓冲策略,因为它符合人类的阅读习惯,可以逐行显示输出内容。

  • 全缓冲(Fully Buffered)
    当缓冲区被填满时,缓冲区中的数据才会被刷新到磁盘或其他存储介质。
    磁盘文件通常采用全缓冲策略,因为它可以累积更多的数据一次性写入磁盘,从而提高效率。

  • 用户强制刷新
    通过调用特定的函数(如 C语言中的 fflush 函数)来强制刷新缓冲区中的数据。
    这在需要立即将缓冲区中的数据写入磁盘时非常有用。

  • 进程退出时刷新
    当进程退出时,操作系统通常会自动刷新缓冲区中的数据,以确保所有数据都被正确写入磁盘。
    这是一种隐式的刷新机制,不需要用户显式调用刷新函数。


4. 总结

 在这篇文章中,主要向大家介绍了重定向的概念,方法以及缓冲区这一重要概念,希望大家有所收获。😊

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

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

相关文章

AI技术和大模型对人才市场的影响

012024 AI技术和大模型 2024年AI技术和大模型呈现出多元化和深入融合的趋势,以下是一些关键的技术方向和特点: 1. 生成式AI 生成式AI(Generative AI)在2024年继续快速发展,它能够创造全新的内容,而不仅仅…

Redis——有序集合

目录 1. 添加元素 ZADD 2. 查看全部元素 ZRANGE 3. 查看某个元素的分数 ZSCORE 4. 查看元素的排名 ZRANK SortedSet 也叫 ZSet ,即有序集合, 有序集合与集合的区别: 有序集合的每个元素都会关联一个浮点类型的分数,依赖该分数的的大小对…

《Milvus Cloud向量数据库指南》——多模态融合新纪元:音频、视频与文本的无缝转换

在探讨多模态数据处理与应用的广阔领域中,多模态文本、音频、视频数据的融合与交互成为了近年来人工智能研究的热点之一。这一趋势不仅推动了技术的深度发展,也为众多行业带来了前所未有的创新机遇。本文将深入剖析多模态文本-音频与多模态文本-视频RAG(Retrieval-Augmented…

书生大模型基础岛-第三关:LangGPT结构化提示词编写实践

1.来源和任务 来源: https://github.com/InternLM/Tutorial/blob/camp3/docs/L1/Prompt/task.md 任务: 背景问题:近期相关研究发现,LLM在对比浮点数字时表现不佳,经验证,internlm2-chat-1.8b (internlm2-…

C++——list容器以及手动实现

LIST容器 list概述列表容器属性例子 list函数构造函数默认构造函数:带有元素个数和元素初值的构造函数:范围构造函数:拷贝构造函数:移动构造函数:示例 赋值运算符重载拷贝赋值操作符 (1):移动赋值操作符 (2…

安全通信|数据加密的由来|加密算法简介|中间人攻击与证书认证|身份验证

👈️下一篇 计算机网络-专栏👈️ 数据加密的由来|加密算法简介|中间人攻击与证书认证 引言 在客户端(client)-服务器(server)模式下,客户端与服务器间通信,如果明文传输数据,在传输过程被劫持,内容直接泄…

MySQL触发器和存储过程

1、触发器 (1):建立触发器,订单表中增加订单数量后,商品表商品数量同步减少对应的商品订单出数量,并测试 mysql> create trigger orders_after_insert_trigger-> after insert on orders for each row-> up…

不知道你们有没有我这样的一种状态...总是这样又总是那样...

各位小伙伴们,我是风尚,我不知道你们有没有那么一刻,就是感觉站在人生的十字路口,感觉自己就像是被扔进了一个没有导航的迷宫,四周都是墙,头顶是蓝天白云,却怎么也找不到出口的方向?…

重磅推荐!GBD再度登顶Lancet!| GBD数据库周报(7.17~7.23)

全球疾病负担(GBD)是迄今为止规模最大、最全面的一项研究,旨在量化不同地区和不同时期的健康损失,从而改善卫生系统并消除差异。 该研究由华盛顿大学健康指标与评估研究所 (IHME) 牵头,是一项真正的全球性研究&#xf…

MySQL--数据库与表的操作

前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 数据库的基本操作 # 1、查看数据库show databases;​# 2、创建数据库create database 数据库名称;​# 3、删除数据库drop databse 数据库名称; 数据表…

RAC(Teamcenter )开发,Bom行解包和打包的方法

1、打包 UnpackAllAction allAction new UnpackAllAction((AbstractBOMLineViewerApplication) currentApplication, "packAllAction"); new Thread(allAction).start();2、解包 UnpackCommand command new UnpackCommand(bomLine); command.executeModal();3、注…

Marin说PCB之Orcad Capture调网表时出现了“Duplicate Pin Name”该怎么搞?

最近大巴黎在如火如荼的举行着奥运会,中国健儿们也是不负众望在很多项目中取得金牌的好成绩,其中中国选手陈芋汐/全红婵夺得巴黎奥运会跳水女子双人10米台金牌,其实没有看我就知道比赛的结果了,肯定是我们中国队夺得金牌的。 看到…

月薪竟然高达60k,AI大模型凭什么?

你是不是最近经常看到或听到“AI大模型”这个关键词?我也是!所以好奇去Boss直聘上搜了下工作机会。看到结果时,我有点不淡定了!薪资竟然这么高! 这是我随便搜的结果,发出来给大家看看。 下面,我…

多线程习题

1.使用两个线程完成两个文件的拷贝&#xff0c;分支线程1拷贝前一半&#xff0c;分支线程拷贝后一般&#xff0c;主线程回收两个分支线程的资源 #include<myhead.h> struct Buf {const char *srcfile;const char *destfile;int start;int len1; }; //创建求源文件大小的函…

【C#】.net core 6.0 webapi 使用core版本的NPOI的Excel读取数据以及保存数据

欢迎来到《小5讲堂》 这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 背景读取并保存NPOI信息NPOI 插件介绍基本功能示例代码写入 Excel 文件…

继电器测试中常见的故障和解决方法有哪些?

在继电器测试中&#xff0c;可能会遇到诸如电源问题、负载问题、控制系统问题和显示问题等故障。如果电源电压不稳定或波动过大&#xff0c;可能会导致继电器测试负载箱无法正常工作。此时需要检查电源线路&#xff0c;确保电源电压稳定在规定范围内。如有必要&#xff0c;可以…

详细说明Java中Map和Set接口的使用方法

Map与Set的基本概念与场景 Map和set是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例化子类有关。以前常见的搜索方式有&#xff1a; 1. 直接遍历&#xff0c;时间复杂度为O(N)&#xff0c;元素如果比较多效率会非常慢。 2. 二分查找&#x…

PSINS工具箱函数介绍——insinit

insinit是初始化INS系统的函数 函数需要用到PSINS工具箱&#xff0c;关于工具箱的一些入门知识&#xff0c;参考这篇文章&#xff0c;是关于工具箱的讲解&#xff1a; PSINS初学指导&#xff1a;https://blog.csdn.net/callmeup/article/details/137087932 函数使用方法 正…

vulhub:Apache解析漏洞apache_parsing

在Apache1.x/2.x中Apache 解析文件的规则是从右到左开始判断解析&#xff0c;如果后缀名为不可识别文件解析&#xff0c;就再往左判断。如 1.php.xxxxx 漏洞原理 Apache HTTPD 支持一个文件拥有多个后缀&#xff0c;并为不同后缀执行不同的指令。比如如下配置文件 AddType te…

蓝牙网关北京厂家_蓝牙网关型号价格介绍

蓝牙网关北京厂家介绍&#xff1a;北京桂花网科技有限公司成立于2015年&#xff0c;开发了远距离蓝牙网关&#xff0c;重新定义了蓝牙&#xff0c;拓展了蓝牙的应用范围&#xff0c;在2016年1月的拉斯维加斯世界消费电子CES展会上一举夺得Best of CES创新大奖。随后&#xff0c…