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

【Linux手册】从接口到管理:Linux文件系统的核心操作指南

前言

我们日常进行代码编写解决大型项目时,有极大一部分时间都是在与文件进行打交道,尤其是在Linux下开发。所以掌握文件的基本操作是必须的,理解访问文件的本质能够让我们更深刻的理解操作系统看待文件的方式。本文将从三方面来讨论如何对文件进行操作,以及文件的本质是什么。

  • 文件的接口;
  • 访问文件的本质;
  • Linux下一切接文件是什么意思。

在学习文件的知识之前,有几个共识的原理会贯穿文章:

  1. 文件=文件内容+文件属性;
  2. 文件分为被打开的文件和没被打开的文件;本文重点谈论被打开的文件,即被进程打开的文件;

C语言的文件接口

C语言中文件接口有很多,此处只介绍几个基本的接口。

FILE* fopen(const char* path,const char* mode)

在这里插入图片描述

fopen用来打开文件:

  1. 返回值FILE*一个指针:指向一个结构体,该结构体专门用来存储打开文件的各个信息;
  2. 第一个参数path:打开文件所处的路径,如果没有指明路径默认文件在当前路径下查找或创建,即进程当前所在的路径;
  3. 第二个参数mode,打开文件的方式。
选项效果没有该文件怎么办补充
“r”: read打开文件,只读不会创建,打开失败,返回NULL
“r+”打开文件,可读可写不会创建,打开失败,返回NULL
“w”: write打开文件,只写会创建新文件会先将文件清空再写入
“w+”打开文件,可读可写会创建新文件会先将文件清空再写入
“a”: append打开文件,只写会创建新文件在文件末尾追加,不清空
“a+”打开文件,可读可写会创建新文件在文件末尾追加,不清空

在这里插入图片描述

与fopen相反的就是int fclose(FILE* pf)用于关闭文件。

size_t fwrite(const void* ptr , size_t size , size_t nmemb , FILE* stream)

在这里插入图片描述

fwrite进行写操作,先已经打开的文件中进行写入,关于写入的方式是追加还是覆盖由文件打开的方式决定:

  1. 第一个参数:const void*类型的指针,指向要写入的数据起始位置;
  2. 第二个参数:size_t类型的数据,存储写入数据的基本单元的大小;
  3. 第三个参数:还是一个size_t类型的数据,存储要写入的基本单元的数量;
  4. 第四个参数:FILE*类型的指针,指向要写入的文件FILE结构体;
  5. 返回值:成功写入的基本单元的个数。

补充:关于数据的基本单元和基本单元的数量

关于数据的基本单元和基本单元的数量实际上并没有太严格的限制:对于一个字符串来说可以把字符看作基本单元,也可以把整个字符串看作一个基本的单元。只要两者相乘等于要写入的字节总数即可,原因在于其本质上调用的是write系统调用接口,该接口只看字节总数。

补充:在写入字符串的时候,需不需要把\0有写入到文件中???

const char ptr[]="hello world;
fwrite(ptr,strlen(ptr),1,pf);
fwrite(ptr,strlne(ptr)+1,1,pf);

以上两个fwrite哪一个是对的???
答案:不用加\0即第一个是对的,因为\0是C语言中字符串结束的表示,但是这和文件没有任何关系;文件可能被各种语言读取,每个语言都有自己的规则,所以文件不能直接用C语言的规则。
在这里插入图片描述

int fprintf(FILE* stream , const char* format…)

fprintf也是对数据进行写入,但是fprintf更好用,其使用起来与printf一样,只不过在第一个参数加上指向文件对应的FILE结构体即可。

在这里插入图片描述


文件系统接口

库函数是对系统调用接口的封装使得程序员开发更加方便,在C语言中printf/fprintf/fgets…都是对系统调用接口的封装,下面介绍一些常见的文件系统接口。
不同语言的文件操作接口千变万化,但是对于操作系统来说都是同一个接口调用的

int open(const char* pathname,int flags ,mode_t mode)

open是一个操作系统提供的用来打开文件的接口。
在C语言中的fopen就是对open进行的封装,不论是任何语言只要是在Linux下打开文件的接口其底层必定是调用的open函数。

open的各个参数:

  1. 第一个参数pathname:打开文件所处的位置,绝对路径/先对路径,如果没有知名具体路径就默认在当前路径中打开;
  2. 第二个参数flags:打开文件的模式/选项,具体选项可将下标:
选项作用
O_RDONLY只读
O_WRONLY只写
O_RDWR即读又写
O_CREAT没有文件进行创建
O_TRUNC有文件将文件内容清空
O_APPEND在文件末尾追加
关于以上选项实际上就是个宏定义,实际上就是八进制的数字:在这里插入图片描述

使用比特位对应的不同的数值来表示不同的含义,因为是在不同的比特位上,所以可以通过按位或|将选项进行叠加使用,比如O_WRONLY|O_CREAT表示以写的方式打开文件,如果没有文件就创建新文件。
3. 第三个参数是用来调整新创建的文件权限的,一般普通文件权限就是0666,目录文件就是0777,注意还要取出权限掩码对应的权限。
4. 返回值,open返回值是一个int的整形,操作系统通过这一个整形来确定后续要操作的文件,该整形被称为文件标识符,后面会具体讲解。
通过open的各个参数的解释,我们大概可以感觉到C语言库函数中的fopen是如何进行封装的了,下面写一个简单的demo代码,演示下库中fopen的实现方式:
在这里插入图片描述

int close(int fd)

close关闭文件,参数是文件描述符。这个就不多说了。

ssize_t write(int fd , const void* buf , size_t count)

write操作系统提供的用来先文件中写入的接口。

  1. 参数一fd:文件描述符,决定了对哪一个文件进行操作;
  2. 参数二buf:指向要写入数据的起始位置;
  3. 参数三count:决定写入数据的字节总数。

ssize_t read(int fd , const void* buf , size_t count)

与write的操作方式一摸一样。


操作系统如何管理文件

操作系统管理的底层逻辑一律是:先描述,再组织。
Linux下又struct files_struct来描述一个进程打开的文件信息:
在这里插入图片描述

其中有两个数组是需要特别关注的:struct file ** fd和struct file * fd_array[NR_OPEN_DEFAULT],他们两个的作用是一样的,类似于动态顺序表和静态顺序表,其中一级指针类似于静态顺序表常用于打开文件少的时候来减少空间的开辟,二级指针类似于动态顺序表常用于打开文件多。该数组每个位置的下标就是对应着已经打开文件的文件描述符,open系统调用接口的返回值就是数组对应的下标

这两个结构体指针都指向struct file *的数组,struct flie是专门用来记录每个文件的各种属性的:
在这里插入图片描述

该结构体又包含其他很多的结构体,其中struct list_head f_list是用来记录所以系统打开的文件,在这里插入图片描述

就是一个双向指针,通过该双向指针可以实现所有文件属性的查找 ;f_count是文件的引用引用计数,记录有多少个进程打开这个文件,f_flags用来记录文件被打开的方式/选项,就是open中的flag,f_mode记录文件的权限,f_error记录操作文件的错误码,还有一个比较重要的就是struct address_space *f_mapping在后面的内存管理中会终点介绍。总而言之struct file中存储着打开的文件中的各种信息。

示意图如下所示:
在这里插入图片描述

文件描述符的填写规则:从0开始寻找最小的没有被使用的小标来使用。

补充

在学习C语言的时候我们知道,C语言会默认打开三个流分别是stdin,stdout,stderr,因为Linux下一切接文件,这三个流也是文件,所以其一定也在struct files *的数组中。确实是这样的,并且这三个流分别占用数组的前三个下标。

C语言为什么要打开这三个流,是C语言的特性吗???其他语言有没有???

所有语言都有这一特性,这三个流的使用太频繁了,所以进程加载后都会默认打开这三个文件,其不是C语言的特性,而是操作系统的特性。实际上这三个文件默认就是打开的状态,,操作系统只是在启动进程以后,将这三个文件填写到进程的文件描述符表对应的下标位置而已。

*close()关闭文件需要进行那些操作???

  1. 将文件struct file中的count引用计数-1,当引用计数为0时,操作系统对文件进行回收;
  2. 将进程的文件描述符表对应的位置悬空。
http://www.xdnf.cn/news/14975.html

相关文章:

  • 《C++初阶之内存管理》【内存分布 + operator new/delete + 定位new】
  • 访问Windows服务器备份SQL SERVER数据库
  • AI【应用 03】Windows环境部署 TTS CosyVoice2.0 详细流程记录(Matcha-TTS、spk2info.pt等文件分享)
  • 从品牌附庸到自我表达:定制开发开源AI智能名片S2B2C商城小程序赋能下的营销变革
  • iOS 抓包详细教程:从零搭建、操作到实战调试的全流程指南
  • Fiddler中文版全面评测:功能亮点、使用场景与中文网资源整合指南
  • 网安系列【15】之Docker未授权访问漏洞
  • 微信小程序控制空调之EMQX服务器安装与配置
  • 在 Apple 生态中,`aarch64` 和 `arm64` 本质上是相同的架构
  • 亚马逊首个“海折节”,缘何加码进口电商?
  • 使用 FreeRTOS 实现简单多任务调度(初识 RTOS)
  • HarmonyOS学习记录4
  • 基于SpringBoot+Vue的疫情问卷调查与返校信息管理系统】前后端分离
  • Paimon 原子提交实现
  • 19-C#静态方法与静态类
  • 桌面开发,在线%图书管理系统%开发,基于C#,winform,界面美化,mysql数据库
  • Foundry智能合约测试设计流程
  • Git系列--3.分支管理
  • 学习open62541 --- [79] 在docker中运行open62541工程
  • Java零基础笔记08(Java编程核心:面向对象编程高级 {继承、多态})
  • 编写产品需求文档:黄历日历小程序
  • Python-FAQ-单例模式
  • 论文解析篇 | YOLOv12:以注意力机制为核心的实时目标检测算法
  • 从零用java实现 小红书 springboot vue uniapp(13)模仿抖音视频切换
  • 飞算AI-idea强大的AI工具
  • 函数-3-日期函数
  • Android ViewModel机制与底层原理详解
  • 深度学习 必然用到的 微积分知识
  • 整合Spring、Spring MVC与MyBatis:构建高效Java Web应用
  • 【实习篇】之Http头部字段之Disposition介绍