当前位置: 首页 > news >正文

文件描述符fd 和 缓冲区

目录

1.文件描述符 fd

1.1文件打开的返回值fd(重点)

1.2.如何理解Linux下的一切皆文件

1.3.文件fd的分配原则 && 输出重定向

1.4.dup2()函数

2.缓冲区

2.1. 概念

2.2. 存在的原因

2.3. 类型(刷新方案)

 2.4. 存放的位置


1.文件描述符 fd

1.1文件打开的返回值fd(重点)

我们可以看到,这三大数据流是来自于C标准库的,类型又是一个我们从来没有听说过的FILE类型。我们可以预料就是,FILE类型应该是C语言库函数内自己封装的一个函数结构体。 

  

我们看到这个数组的下标模式,也大概对文件的管理形式有了一定的猜测。

  1. 首先是对所有的文件,都有一个描述,即结构体对文件进行描述。
  2. 然后,有一个数组存储了这些文件结构体的地址对他们进行管理。
  3. 最后,task_struct 内部有一个指针指向该数组 。

就是如下图这种管理模式

 

1.2.如何理解Linux下的一切皆文件

其实上一切皆文件,就是封装的结果,利用形式上完全一模一样的结构体对各种硬件进行描述,让其截然不同的操作封装硬件提供的接口上(读写。。操作,每一个硬件都封装了这样的函数)通过,函数指针指向接口,其中的返回值,函数指针名等都一致,只是函数的底层实现不一样罢了。这样底层的不一致就被屏蔽了,在我们使用者看来就是一切皆文件了,也就是一切皆struct file

1.3.文件fd的分配原则 && 输出重定向

 fd分配规则:最小的没有使用的数组下标会分配给刚刚打开的文件。

看到这里,是不是很奇怪,明明printf()是向显示器写入的为什么会对log.txt写入呢? 

这数组的前三个在操作系统打开的时候,加载进来了,而printf()函数,默认就是向该数组中fd为1的文件执行写入操作,我们把fd==1的文件关了,重新加载了一个文件进去,按照分配规则,那么那个文件就该被分配为fd==1. 

1.4.dup2()函数

  1. 参数:oldfd:要被复制的文件描述符;newfd:目标文件的描述符。

  2. 返回值:成功,返回新的文件描述符(即:newfd)。出错时,返回 -1,并设置 errno以指示错误。

功能:就是将新的文件描述符变成旧的文件描述符的一份拷贝拷贝。

int main()79 {80   int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);81 82   dup2(fd,1);83   printf("hello Linux\n");84 85   close(fd);                                                                                                                                                        86 87 }

 

2.缓冲区

2.1. 概念

本质是一块内存区域,用于暂时存放数据,以便更高效地处理输入、输出操作。

  • 此处的缓冲区(如:进度条中的缓冲区等),不是内存中的缓冲区,它是语言层面的缓冲区,即:C语言自带的缓冲区,由C语言标准库提供。

缓冲区也会为格式化输入、输出操作提高场所。

  • printf函数工作原理:它会将其他类型的数据(如整数、浮点数等)转换为字符数据(即字符串),转化后的数据会被写入到FILE结构体维护的缓冲区中,根据条件刷新缓冲区。
  • scanf函数工作原理:scanf会从输入流中读取字符数据,将读取的数据转化为相应的格式化数据,格式化的数据会被存放到FILE结构体维护的缓冲区中,最终被存放到变量中。


2.2. 存在的原因

提高使用者的效率

  • 减少了C接口的使用时间,从而减少了用户的等待时间,提高了使用者的效率:调用C接口时,只要将数据交给了缓冲区,就可立即返回,无需等待实际的写入操作完成,意味这用户可以更快地继续执行其他任务。

提高计算机整体的拷贝效率。

  • 调用系统调用接口,都是有成本的,有时间和空间的开销。
  • 减少调用系统调用的次数,提高了计算机整体的拷贝效率:缓冲区可以聚集大量数据,直到缓冲区满了,再调用一次系统调用进行实际的数据写入,即:进行一次拷贝。

2.3. 类型(刷新方案)


一、无缓冲、无刷新

无缓冲:无刷新,意味着数据不会暂存在缓冲区中,而是立即被写入到目标设备中。

适用场景:需要立即看到结果、实时性要求很高的场景,如:实时系统、设备驱动程序。

优点:保证了数据的即时可见性。

缺点:性能下降,频繁的使用系统调用会增加开销。

二、全缓冲、全刷新

全缓冲:全刷新,缓冲区满了或者关闭文件时,缓冲区的数据才会被刷新到目的设备中。

适用场景:文件的读写操作,尤其是大文件。

优点:减少了系统调用的次数,提高了性能。

缺点:可能会丢失数据,如:在缓冲区的数据未被刷新前,发生崩溃,则这部分的数据就会丢失。

三、行缓冲、行刷新

行缓冲:行刷新,意味着遇到换行符\n,缓冲区的数据就会被立即刷新到目的设备中。

适用场景:标准输入输出(显示器)。

注:当调用c语言接口fflush(),进行强制刷新; 进程退出时,或文件关闭时,自动刷新


 2.4. 存放的位置

  1. 缓冲区存放在FILE结构体中,即:缓冲区是被FILE结构来维护的。
  2. 每个通过标准C库函数打开的文件,都拥有自己的缓冲区。

fwrite等标准库函数,会先将数据拷贝到缓冲区中,然后根据一定的条件,调用系统调用接口进行刷新。

文件操作的系统调用接口,其实是个拷贝函数,它将数据从语言层的缓冲区拷贝到内存的缓冲区。

int main()80 {81  87 88   const char * s1="hello write\n";89 90   write(1,s1,strlen(s1));91 92   const char * s2="hello fprintf\n";93   fprintf(stdout,"%s",s2);94 95   const char * s3="hello fwrite\n";96   fwrite(s3 ,strlen(s3),1,stdout);97 98   fork();99 return 0;                                                        
}

现象1解释:write()为系统调用接口,直接将数据写入到内核中;fprintf、fwrite为库函数,先将数据写入到缓冲区中,因为它们都是向显示器进行写入,而写入显示器是行刷新(遇到换行符\n,进行刷新),所以fork创建子进程前缓冲区中的数据全部被刷新到内核中了。

刷新到内核的数据,不属于进程的数据;存放在缓冲区中的数据,属于进程的数据。

现象2解释:重定向到普通文件时,数据刷新缓冲区的方式,由行缓存变为全缓冲,C语言接口自带缓冲区,所以它会将数据写入到缓冲区中,就不会立即刷新。fork创建子进程,父子共享缓冲区的数据,但是进程退出后,统一进行刷新。刷新缓冲区,是清空缓冲区,是修改数据的一种方式,所以父子进程的数据会发生写时拷贝,父子进程分别刷新各自的缓冲区,随即产生两份数据。write是系统调用接口,直接将数据写入到内核中,不存在所谓的缓冲区。

  1. 一般C库函数写入文件时,是全缓冲; 写入到显示器时,是行缓冲。
  2. 重定向到普通文件时,数据刷新缓冲区的方式,由行缓存变为全缓冲。
  3. 刷新缓冲区,是清空缓冲区,是修改数据的一种方式


http://www.mrgr.cn/news/63684.html

相关文章:

  • 迷茫内耗的一天
  • 硅谷甄选(8)spu
  • 使用语言模型进行文本摘要的五个级别(llm)
  • linux基本指令之文件操作
  • Spring Boot 集成 Kafka
  • 树莓派基本设置--2. raspi-config工具介绍
  • javaScript-----一维数组和数组对象去重的多种方法
  • 【数据结构】guard
  • 【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-10
  • 虚拟机 Ubuntu 扩容
  • 二十八、Python基础语法(面向对象-下)
  • C. Gorilla and Permutation
  • 打响反对人工智能的第一枪
  • 多处理机调度(李昂学长视频总结)25新增考点
  • 音视频入门基础:FLV专题(22)——FFmpeg源码中,获取FLV文件音频信息的实现(中)
  • 奇瑞不客气智驾 晚不晚?
  • 浅谈DDD(领域驱动设计)
  • 齐次线性微分方程的解的性质与结构
  • 云计算的优势及未来发展趋势
  • 【Leecode】Leecode刷题之路第37天之解数独
  • 1007:计算(a+b)×c的值
  • 事件的传递
  • java基础知识21 异常处理try与throw的相互处理e.getcause
  • 第一讲 递推与递归
  • 【qt qtcreator使用】【正点原子】嵌入式Qt5 C++开发视频
  • 一些swift问题