详解文件篇(待更)

news2024/11/16 15:55:00

目录

    • 前言
    • 一、背景知识
        • 1.文件的真面目
        • 2.对文件进行的操作
        • 3.所谓的打开文件究竟是在干啥?
        • 4.文件可能存在的位置?
        • 5.文件操作的幕后主使者
    • 二、复习C语言中的相关文件操作
        • 1. 源代码:形成一个file.txt的文件
        • 2. 以"r"的方式打开文件
        • 3. 以"w"的方式打开文件
        • 4. 以"a"的方式打开文件
        • 5.文件形成的路径
    • 三、操作系统中的相关文件操作的系统调用接口
        • 1. open
        • 2.write
        • 3.read
    • 四、文件描述符
        • 1. 查看open函数的返回值
        • 2.实验:验证0,1,2和C语言中的stdin,stdout,stderr的关系
        • 3.文件描述符的本质
        • 4.文件描述符的分配规则

前言

在前面,我们学习C语言的时候已经学习过相关的对文件进行操作的函数接口,我们知道对文件进行的操作最常见的有打开文件关闭文件对文件进行读写操作,那么今天我们需要学习的是操作系统层面上的对文件进行操作的理解,会进一步加深对文件的理解和学习

一、背景知识

1.文件的真面目

一个文件是由其文件内容文件属性共同构成的,在计算机中**,我们将一个文件的文件内容和文件属性统称为文件的数据**,一个文件一旦被创建,它存在的地方就有可能是磁盘内存,当一个文件没有被打开的时候,这个文件是在磁盘中静静地躺着的,当一个文件被打开的时候,是会由相应的进程将这个文件加载到内存,此时,文件存在的位置就是内存

2.对文件进行的操作

由上面的介绍我们知道,一个文件是由文件内容文件属性构成的,因此对文件的操作本质上就是对文件内容的操作对文件属性的操作,我们需要注意的是,有时我们在修改文件的内容的时候,有可能也会影响到文件的属性数据,比如:当我们对一个文件的内容进行修改的时候,那么修改完成之后,这个文件的内容明显会发生变化,那么这个文件的修改时间和文件的大小等也会发生变化,这些数据就属于文件的属性数据

3.所谓的打开文件究竟是在干啥?

这个过程其实是在将文件的属性数据或者内容数据加载到内存,因为,当我们要执行这个文件的时候,明显是需要CPU来处理这个文件的,需要CPU读取相关的指令从而对这个文件的进行读写操作,而我们知道,由冯诺依曼体系可知**,CPU在数据层面上是不与外设直接接触的**,当一个文件没有被打开的时候,那么这个文件是存放在磁盘上的,磁盘属于一种外设。因此如果我们想要打开一个文件从而对其进行读写操作,首先肯定是需要将这个文件先加载到内存,然后才能上CPU运行

4.文件可能存在的位置?

一个文件被创建的时候,这个文件是处于关闭状态的,那么此时这个文件存在的位置是磁盘磁盘文件),而由上面一条知识点我们知道,当我们在打开文件的时候,本质是操作系统正在将这个文件加载到内存,从而此时这个文件我们可以理解为正在被运行,此时这个文件存在的位置就是内存(打开的文件)。因此文件存在的位置可能是磁盘也可能是内存

5.文件操作的幕后主使者

我们知道,对文件进行的操作主要是打开文件关闭文件对文件进行读写操作,那么这些操作本质上到底是谁在执行的呢?我们平时写的C语言函数中使用到一些函数接口来进行这些操作,但是我们写完后发现这些文件操作并没有被执行,当我们将写的源代码编译形成可执行程序之后,对应的文件操作依旧没有被执行,当我们将可执行程序运行起来的时候相应的文件操作才能被执行起来,我们知道,当一个可执行程序运行起来的时候,本质上是在内存中创建了对应的进程,因此文件的操作本质上是进程在对文件进行操作

二、复习C语言中的相关文件操作

1. 源代码:形成一个file.txt的文件

在这里插入图片描述
这个源代码的作用是会在当前路径下形成一个叫file.txt的文件,并且这个代码中打开文件的方式是"w",就是以写的形式打开,我们需要知道的是常见的打开文件的方式有三种,分别是"r","w","a",那么这三种打开文件的方式分别是以读的方式打开以写的方式打开以追加写的方式打开,区别在于第二个以写的方式打开的时候呢,如果文件不存在那么会创建对应的文件,并且如果文件存在,文件中存在内容,则首先会将该文件中的内容清空,再从文件的头部重新写入,第三个以写的方式打开,不会清空文件中的内容,而是会在文件的末尾继续追加内容到文件中

下面的实验中,对file.c源文件编译之后形成的可执行程序叫file,对file2.c编译之后形成的可执行程序叫做file2,对file1.c编译形成可执行程序叫file1

2. 以"r"的方式打开文件

  • 源代码
    在这里插入图片描述
  • 运行结果
    在这里插入图片描述

上面的实验中,我们首先使用file形成对应的文件,再使用file2中以读的方式继续打开该文件,实验现象是可以正常将文件的内容输出到显示屏上,并且不会将文件的内容清空

3. 以"w"的方式打开文件

  • 源代码
    在这里插入图片描述
  • 实验现象
    在这里插入图片描述
    我们首先将file文件运行起来,这里首先会形成一个file.txt文件到当前路径下,但是当我们将file1运行起来的时候,我们再次打开这个file.txt文件的时候,我们发现观察不到任何的内容,此时证明这个文件被清空了

4. 以"a"的方式打开文件

  • 源代码
    在这里插入图片描述

  • 实验结果
    在这里插入图片描述
    从最终的实验现象中我们可以看到外面再file3程序中为file.txt文件中写入的内容最终被写在了上次运行之后的file.txt中已经形成的文件之后

5.文件形成的路径

当我们文件的时候其实是需要指明这个文件存在的路径的,那么如果没有指明这个文件存在的路径,那么就会默认在当前路径下形成这个文件,当前路径指的就是当前进程所在的路径,这个我们可以使用ls /proc/pid -l进行查看:
在这里插入图片描述

  • cwd:指的是当前进程的路径,其实就是correct work directory,就是当前的工作路径
  • exe:指的是当前可执行程序的路径

三、操作系统中的相关文件操作的系统调用接口

1. open

  • 查看系统手册
    在这里插入图片描述
    通过这里的手册的介绍,我们可以知道,open这个接口的作用是打开文件,这个文件有三个头文件,然后手册中介绍了三个使用方法的声明方式,我们最常用的是其中的第二个声明方式,其中的第二个声明方式有三个参数,第一个参数是文件的名字(带路径),第二个参数是标记位(后面会重点介绍),第三个参数是设置文件的权限
  • open中的flags详解
    这个flags的作用是告诉操作系统我们要以什么方式打开文件,其中最常见的标记位有:O_RDONLY:只读
    O_WRONLY:只写
    O_RDWR:读写
    O_APPEND:追加
    O_CREAT:不存在就创建
    O_TRUNC:清空

    然后这个标记位是以位图的形式传参的,下面举一个例子来辅助理解
  • 源代码
    在这里插入图片描述
  • 实验结果
    在这里插入图片描述
    在上面,我们定义了几个宏,其实分别可以表示一个整数中的几个比特位,1表示0001,2表示0010,4表示0100,8表示1000,其中如果要传一个,那么直接传入即可,如果要传多个参数,那么需要用按位或进行传参,才能够将多个逻辑对应起来
  1. 打开文件(读文件)
  • 源代码
    在这里插入图片描述
  • 实验结果
    在这里插入图片描述
    能够在当前路径下形成了一个file.txt的文件
  1. 写文件
  • 源代码
    在这里插入图片描述
  • 实验结果
    在这里插入图片描述
    上述实验成功将hello mywrite字符串写入到文件中,调用的是系统调用接口write
  • 查看write
    在这里插入图片描述
  • 注意:
    在这里插入图片描述
    我们会发现,上述的代码执行两次之后得到的是同样的结果,这是因为上述的代码再重新执行程序打开文件的时候会先将文件中的内容清空,再重新进行写入,我们可以稍微代码做修改,实验现象会更加明显
    在这里插入图片描述
    我们可以将向文件中写入数据的代码先注释起来,然后查看实验结果
  • 实验结果
    在这里插入图片描述
    我们可以观察到,第二次打开文件的时候,发现文件中的数据已经被清空,这个代码的功能相当于C语言中的以"w"的方式打开文件
  1. 追加写文件
  • 源代码
    在这里插入图片描述

  • 实验结果
    在这里插入图片描述
    通过上面的实验结果我们可以发现,第二次在执行可执行程序二次打开文件的时候,文件的内容新增了,原来的文件内容并没有被清空,上面的代码的作用相当于C语言中学习的以"a"的方式打开文件,就是不会清空原来文件中的数据,而是会重新向文件内容的末尾中追加写入数据

2.write

  • 查看man手册
    在这里插入图片描述
  1. 函数参数
  • fd:代表向哪个文件写入,fd是一个文件描述符
  • buf:代表写入的内容,一般情况下我们是将要写入文件中的内容先放在一个数组中的
  • count:代表写入的内容的字符数
  1. 使用
  • 源代码
    在这里插入图片描述
  • 实验结果
    在这里插入图片描述
    通过上述的实验结果,我们可以看到,我们确实成功在当前路径打开了一个文件,并且成功将我们的内容写入到文件中,其实本质上我们调用write的时候是将数据写入到系统内核的,不是直接写入到磁盘上的也就是所谓的文件,后面我们学习缓冲区的时候会具体说到

3.read

  • 查看man手册
    在这里插入图片描述
  1. 参数
  • fd:指明从哪个文件读取内容
  • buf:读取的内容存放的位置
  • count:读取的内容的字符个数
  1. 使用
  • 源代码
    在这里插入图片描述
  • file.txt中的 内容
    在这里插入图片描述
  • 实验结果
    在这里插入图片描述
    从上面的实验结果来看,我们确实成功将file.txt中的内容读取到并且将其输出到显示器上,源代码中我们需要注意的是,在读取文件内容的时候,我们首先需要准备一个数组来存放我们即将从文件中读取到的数据,然后读取的个数一定不能超过数组最大的空间,并且要预留出一个字节的空间来存放’\0’,read接口调用成功之后会返回实际读取的字节个数,因此那么数组中下标位返回值的位置存放的就是读取的最后一个元素的下一个位置,因此,当我们读取成功的时候,我们需要注意将最后一个元素的下一个位置设置成’\0’

四、文件描述符

1. 查看open函数的返回值

  • 源代码
    在这里插入图片描述
  • 实验结果
    在这里插入图片描述
    实验结论:通过上面的实验结果,我们惊奇的发现open函数的返回值竟然是从3开始的,并且以一定的次序排列下去,这是为什么呢?0,1,2为什么不行呢?
    其实0,1,2默认是被打开的,并且都是具有一定意义的,0代表的是标准输入,对应的硬件设备是键盘,1代表的是标准输出,对应的硬件设备是显示器,2代表的是标准错误,对应的硬件设备是显示器

2.实验:验证0,1,2和C语言中的stdin,stdout,stderr的关系

  • 查看stdin,stdout,stderr
    在这里插入图片描述
  • 源代码
    在这里插入图片描述
  • 实验结果
    在这里插入图片描述

3.文件描述符的本质

为什么文件描述符是一个个的自然数呢?为什么不能是其他类型的数据呢?文件描述符的本质是什么呢?想要回答这些问题就必须深入了解系统文件之间是怎么组织起来的

  1. 通过上面的实验,我们知道一个进程是可以打开多个文件的,也就是说系统中进程数与打开的文件数的比例是小于1的,也就是说系统中被打开的文件的数量是非常多的,那么通过前面的学习我们知道,系统中的进程数量也是非常多的,所以系统需要对进程进行管理,那么被打开的文件的数量比系统中的进程的数量还要多,那么需不需要进行管理呢??肯定是需要的,不然系统中的文件岂不是乱套了,那么如何进行管理呢??这个时候我们就需要类比我们在学习进程管理的时候的相关策略了,也就是先描述再组织,所谓的先描述就是系统需要为每一个打开的文件创建一个结构体,这个结构体中就会存放相应的被打开的文件的相关属性,其实这个就是FILE类型
  2. 我们需要来观察系统级别中进程与文件的关系
    在这里插入图片描述
    系统中进程与文件的对应关系就大概如上图所示,系统会为每一个打开的文件维护一个结构体是FILE类型的,每一个结构体就会有对应的地址,是FILE*类型的,那么这些结构体的地址会存放到一个结构体中的一个数组中,这个数组的下标是从0开始往下排列的,数组中存放对应文件指针的下标就是对应文件的文件描述符,从进程的角度来看,进程的task_struct中有一个指针可以找到这个结构体,那么就能够通过这个指针找到这个结构体中的存放文件指针的数组,那么就能够通过数组下标即文件描述符找到对应的打开的文件,那么打开文件中调用open函数接口返回的整数其实就是这个数组的下标,因此,我们后面再将这个返回值传给read函数或者write函数的时候,那么对应的系统调用不就可以通过这个整数(即文件描述符)找到对应的文件指针,从而找到对应的打开文件了吗?

4.文件描述符的分配规则

  • 源代码(正常情况下)
    在这里插入图片描述

  • 实验结果
    在这里插入图片描述

  • 源代码(将文件描述符为0的文件关闭)
    在这里插入图片描述

  • 实验结果
    在这里插入图片描述

  • 源代码(将文件描述符为1的文件关闭)
    这个代码需要注意一些东西,因为文件描述符为1 对应的文件是显示器,如果将1关闭,那么就不能向显示器中输出信息了
    在这里插入图片描述

  • 实验结果
    在这里插入图片描述
    那么此时printf输出的数据到哪里去了呢?分析过程是这样的,因为我们先将文件描述符为1的文件关闭了,所以在文件描述符数组中,1是最前面空闲的,所以此时会将1分配给新打开的文件,printf是向stdout输出的,但是从printf的角度看,printf函数是C 语言函数,它并不知道操作系统已经将stdout关闭了,它只知道文件描述符1,所以,它会继续向文件描述符为1的对应文件写入,而我们知道文件描述符1已经分配给了新打开的文件,因此,此时printf函数会向新打开的文件写入

  • 源代码(将文件描述符为2的文件关闭)
    在这里插入图片描述

  • 实验结论:通过上面的实验结果我们可以看出,文件描述符并不一定是从3开始的,如果我们手动将0,1,2中的任意一个文件描述符对应的文件关闭的话,那么我们新打开的文件的文件描述符也可以是0,1,2所以文件描述符的分配规则是:从头遍历文件描述符数组,找到第一个未被使用的下标的位置,将其分配给新打开的文件

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

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

相关文章

C++异常详解

文章目录前言一、C语言传统的处理错误的方式二、C异常概念三、异常的使用3.1 异常的抛出和捕获3.2 异常的重新抛出3.3 异常安全3.4 异常规范四、C标准库的异常体系五、自定义异常体系六、异常的优缺点C异常的优点C异常的缺点总结前言 正文开始! 一、C语言传统的处理…

大数据:Hive3.x安装部署和配置

文章目录Hive 简介一,Hive 下载和安装1)下载hive2)安装hive二,Hive 部署1)添加hive-site.xml配置2)上传mysql jdbc驱动到hive安装包lib下3)初始化元数据库4)启动hive5)be…

数字时代,你还不了解数据仓库吗?

事实上,很多人在看到数据仓库的第一眼,就把数据仓库当成了数据库。当然这也很正常,毕竟从名字来看这两者确实区别不大,真正区分两者的是定位、作用等更深一层的东西。今天我们就来详细了解一下数据仓库,从概念、特点、…

【网络】一文带你了解计算机网络基础

我们在学校中,相信大家都学过计算机四门必修课之一的计算机网络,那么今天简单的总结一下计算机网络的基础和原理,有需要朋友,可以点赞收藏。 一文带你了解计算机网络基础1、 网络 7 层架构2、 TCP/IP原理2.1 网络访问层(Network A…

MongoDB的入门及使用

文章目录1、MongoDB组成2、基本指令3、安装Studio 3T4、操作语句db操作collection操作document操作document 查询5、开发使用1、MongoDB组成 MongoDB中有三个概念需要大家了解:数据库、集合、文档 数据库database: 数据库是一个仓库,在仓库中…

【Docker】安装容器(Tomcat/MySQL/Redis)

文章目录Docker安装镜像容器安装 Tomcat安装 MySQL安装 MySQL实战版安装 RedisDocker安装镜像容器 步骤 搜索镜像 拉取镜像 查看镜像 启动镜像 - 服务端口映射 停止容器 移除容器 安装 Tomcat 1、DockerHub上查找Tomcat镜像 docker search tomcat 2、从DockerHub拉取Tomcat镜…

volatile synchronized Lock

参考博客:https://www.cnblogs.com/cg-ww/p/14540450.html https://zhuanlan.zhihu.com/p/563597528?utm_id0 synchronized修饰变量,https://blog.csdn.net/dlf123321/article/details/53515756 详解synchronized关键字,https://zhuanlan.zh…

一文回顾Aleo到底有多卷

年底的Aleo是区块链行业的一场大事,由于融资超过两亿美元这样的光环围绕,Aleo出道即备受关注,网上不少教程也是满天飞,甚至在激励测试开始之前,就已经非常卷了,而大家都在认为激励测试在两三周之后开始&…

java swing(GUI) mysql实现的酒店管理系统源码文档教程

今天给大家演示一下由我修订开发的一款由Java swing mysql实现的酒店管理系统,以前是sqlserver版本的,我修改了驱动,做了整体升级,改成了mysql版本的,系统功能非常完善,实现了酒店宾馆日常的所有操作功能&a…

28-Vue之ECharts-折线图

ECharts-折线图前言折线图特点折线图实现步骤折线图常见效果标记线条控制填充风格紧挨边缘缩放, 脱离0值比例堆叠图前言 本篇来学习下折线图的实现 折线图特点 折线图更多的使用来呈现数据随时间的变化趋势 折线图实现步骤 ECharts 最基本的代码结构准备x轴的数据准备 y 轴…

高级网络应用复习——三层和生成树实验 加 命令

作者简介:一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​​ 目录 前言 一.实验 1. 实验要求 2.实验命令 三层配置 和三层交换机连接…

双十二有哪些高性价比的电容笔?十大电容笔知名品牌

任何东西都有它独特的意义、存在和作用。随着ipad的影响力越来越大,ipad的用户数量也越来越多,所以要提高ipad的性能,就需要一款合适的电容笔。那么,电容笔该选择哪个品牌?我将向大家推荐几款性价比高的电容笔&#xf…

PPa-HA/NH2/NHS/MAL焦脱镁叶绿酸-a修饰叶酸/氨基/活性酯/马来酰亚胺的反应

小编下面给大家分享的科研内容是类PPa-HA/NH2/NHS/MAL焦脱镁叶绿酸-a修饰叶酸/氨基/活性酯/马来酰亚胺的反应,和小编一起来看看! PPa-HA焦脱镁叶绿酸-a修饰叶酸的反应: 以脱镁叶绿酸-a甲酯为起始原料,在二氯甲烷中于不同温度下与N-溴代丁二酰…

ChatGPT面试阿里P6测试开发岗能过吗?

最近ChatGPT爆火,ChatGPT能干什么呢?想必已经看过很多文章了,例如ChatGPT通过美国高考、ChatGPT开发游戏、调试代码、写文章等等。 哈哈,作为一个软件测试博主,我怎么可能不出来搞点事情呢?突发奇想&#…

SpringBoot集成Swagger3接口文档及添加Authorization授权

swagger可以为前端提供接口文档及接口测试功能&#xff0c;后端集成起来很方便&#xff0c;对代码也没有入侵&#xff0c;全注解完成&#xff0c;非常好用。 一、集成基础功能 第一步、添加依赖 <!-- swagger3 --><dependency><groupId>io.springfox</g…

28845-97-8,Ac-K-DAla-DAla-OH

Ac-Lys-D-Ala-D-Ala was tested as substrate in a substrate specificity study of the Streptomyces albus DD-carboxypeptidase. 以Ac-Lys-D-Ala-D-Ala为底物&#xff0c;对albus链霉菌dd -羧肽酶进行底物特异性研究。 编号: 172467中文名称: 三肽D丙氨酸羧肽酶底物CAS号: 2…

块交织器5×5 verilog设计及仿真实现

设计题目要求: 本设计基于verilog语言实现,在quartus II下仿真实现: 仿真如下: 全部代码如下: library ieee; use ieee.std_logic_arith.all; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity decoder is --实体说明 port(datain,clk,clr:IN…

汽车轻量化 | 某SUV车型白车身的轻量化分析案例

作者 | 王山 仿真秀科普作者 导读&#xff1a;轻量化是当今各整车厂在产品开发中无法回避的问题。当考虑到成本与工艺方面时&#xff0c;更是不容易解决的问题。对于高端车型&#xff0c;其较高的设计与生产成本允许其采用先进的轻量化设计与生产工艺&#xff0c;如碳纤维复合…

函数:先序输出叶结点

问题引入 【问题描述】按照先序遍历的顺序输出给定二叉树的叶结点。 【输入形式】以字符形式输入二叉树的结点序列 输入的字符序列为&#xff1a;ABC##DE#G##F### 【输出形式】输出二叉树中叶子结点的先序序列 【样例输入】 ABC##DE#G##F### 【样例输出】 CGF 【输出形式】输出…

分布式操作系统 - 8.分布式容错管理

文章目录1.容错性概述1.1 基本概念1.2 失效&#xff08;失败&#xff09;类型1.3 失效&#xff08;失败&#xff09;模型&#xff08;halting failure&#xff09;1.4 基于冗余的失效屏蔽技术&#xff08;1&#xff09;冗余类型&#xff08;2&#xff09;三模冗余方法&#xff…