【Linux】深入理解Linux文件系统:从C接口到内核设计哲学

news2025/4/20 9:21:04

文章目录

  • 前言
  • 一、C语言中的文件接口
    • 1. 文件指针(句柄)FILE*
      • 以写方式打开文件,若文件不存在会新建一个文件
      • W写入方式,在==打开文件之前==都会将文件内容全部清空
      • 追加写方式,其用法与写方法一致,不同在于a方法可以在文件结尾写入
  • 二、认识文件系统调用
    • Linux下的系统调用open()
      • 第一个参数为文件路径
      • 第二个参数为操作文件的方式
      • 第三个可选参数是更改创建文件的默认权限:
  • 三、访问文件的本质
  • 四、重定向与缓冲区
    • 自定义重定向系统调用接口dup2
  • 再谈“一切皆文件”
    • 1. 外设设备与文件系统的关系
    • 2. 扩展思想:
  • 总结


前言

在计算机系统中,文件由内容数据和元数据属性共同构成。文件的完整生命周期分为两个阶段:

文件状态存储位置管理方式
未打开文件磁盘存储介质文件系统通过inode管理
已打开文件内存内核通过file结构体管理
  • 所有文件操作本质上都是进程与文件系统的交互
  • 打开文件需要将文件属性加载到内存
  • 文件内容采用按需加载策略(延迟加载)

研究文件系统本质是研究进程和文件之间的关系(文件是由进程打开的);未打开的文件存在磁盘上(存储介质),文件要被打开(属性)必须先要加载到内存;


一、C语言中的文件接口

基本输入输出 stdio.h
访问磁盘的过程称之为IO的过程,

1. 文件指针(句柄)FILE*

//C标准库通过FILE结构体封装文件描述符
 FILE *fopen(const char *path, const char *mode)
// mode参数决定了你的访问权限
mode说明特性
“w”写模式(清空文件)文件不存在时创建
“a”追加模式保留原内容,末尾写入
“r”读写模式文件必须存在

以写方式打开文件,若文件不存在会新建一个文件

若没有指定路径,程序会在默认当前路径下创建,当前路径指的是进程的当前路径(使用ls /proc/[pid] 查看到当前进程的cwd)。
在这里插入图片描述
同样的,修改当前进程的工作目录就可以改变创建文件的默认路径。

chdir("home/ys") //修改进程工作路径为home/ys

W写入方式,在打开文件之前都会将文件内容全部清空

在这里插入图片描述

上一个程序疑问:strlen要不要+1?

我们知道写入字符串时需要将\0也写入,我们试验之后发现文本中多了@^这样的乱码,推测这就是\0,只不过vim文本编辑器将其解释成了乱码符号。结论是strlen不需要+1,文件系统没有规定字符串必须以\0结尾

追加写方式,其用法与写方法一致,不同在于a方法可以在文件结尾写入

二、认识文件系统调用

c语言程序在启动时,会默认打开三个标准输入输出流文件:

stdin:键盘设备
stdout:显示器文件
stderr:显示器文件

文件其实是在磁盘上的,由于磁盘是外部设备,访问文件实际上是访问磁盘这样的硬件。不同的语言有不同的文件操作方式,但在底层用的是都是一样的实现方式——都需要调用系统接口open、read、write。

库函数(fopen,printf,fscanf等)访问硬件设备一定会通过系统调用来访问。

Linux下的系统调用open()

在这里插入图片描述

第一个参数为文件路径

  • 若pathname以路径的方式给出,则当需要创建该文件时,就在pathname路径下进行创建。
  • 若pathname以文件名的方式给出,则当需要创建该文件时,默认在当前路径下进行创建。(注意当前路径的含义)

第二个参数为操作文件的方式

方式含义
O_RDONLY以只读的方式打开文件
O_WRNOLY以只写的方式打开文件
O_APPEND以追加的方式打开文件
O_RDWR以读写的方式打开文件
O_CREAT当目标文件不存在时,创建文件

1. O_WRONLY是写方式,但是它并不会新建文件
2. O_CREAT打开文件时清空文件

3. O_APPEND 追加写选项
写入:

const char* message = "hello";
write(fd,message,strlen(message));
//write并不会对文件进行清空式写入。
int fd = open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666); //追加
write(fd,message,strlen(message),);

第三个可选参数是更改创建文件的默认权限:

//eg:
int fd = open("log.txt",O_WRONLY|O_CREAT); 

创建权限错误,所以新建文件时需要告诉接口权限是什么。
在这里插入图片描述

int fd = open("log.txt",O_WRONLY|O_CREAT,0666); 

在这里插入图片描述
这里创建出来的并不是666而是664,应该要想到之前学到的权限掩码(0002)的知识!

比特位级别的传参方式原理:
使用位图的方式,一次向操作系统传递多个标志位

三、访问文件的本质

可以将其类比系统管理进程(struct_task),Linux系统中一切皆文件,因此管理进程势必要通过先描述再组织的方法进行。要描述一个被打开的文件(struct_file),往往需要包含文件路径、文件基本属性(权限、大小、读写位置、访问用户的信息等)、文件的内核缓冲区信息、下一个struct_file的指针

一个进程可能会打开多个文件,那么进程与文件之间又是如何关联的?(1:n)

进程PCB中会存在一个结构体指针struct files_struct *files指向了一个结构体,该结构体存放了一个存放各种文件PCB指针的数组;因为是数组,所以这也解释了为什么open接口返回的是int类型的值了,进程根据这个下标就可以访问对应文件。

如果尝试打印一下返回值,发现文件描述符默认是从3开始的,那么0,1,2是什么文件呢?那就是标准输入输出错误流了!(stdin \ stdout \stderr

int fd = open("demo.txt",O_WRONLY |O_CREAT,0666);
cout << fd << endl; //3

cout << stdin->_fileno << endl;//0
cout << stdout->_fileno << endl;//1
cout << stderr->_fileno << endl;//2

在这里插入图片描述
既然一切皆文件,那么输出流也是文件,因此我们可以使用以下代码向标准输出流文件中写入message信息:

const char* message = "hello";
write(1,message,strlen(message));// 1 就是标准输出流stdout

从标准输入流文件中读取buffer大小的字符放在buffer[1024]数组中 :

char buffer[1024];
read(0,buffer,sizeof(buffer));
printf("echo: %s\n",buffer);

四、重定向与缓冲区

文件描述符对应的分配规则是什么?

从0下标开始,寻找没有被使用的数组位置,它的下标就是新文件的文件描述符值。

假设我们有一个空文件log.txt,有如下代码,含义是将msg中的strlen长度的数据输出到显示器。

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

但如果先关闭了1描述符(即关闭标准输出流),除了显示器无法显示外

close(1);
int fd = open("log.txt", O_RDONLY | O_CREAT, 0666);//1
const char* msg = "hello linux\n";
write(1,msg,strlen(msg));//此时写入的就是1号文件描述符,即log.txt 文件

log.txt中居然存有数据。
这一工作,称为输出重定向。根据上面的知识可以意识到关闭了1描述符后,那么这里就是空着的,当使用open接口新建log.txt时,根据文件描述符分配规则,自然1号位就成为了log.txt的fd描述符,所以将本来要写入stdout的数据写入到了log.txt中。

自定义重定向系统调用接口dup2

int dup2(int oldfd,int newfd)
把oldfd复制到newfd
//oldfd 相当于 原本的 3 描述符
//newfd 相当于 原本的 1 描述符


int fd = open("log.txt", O_WRONLY|O_CREAT, 0666);
dup2(fd, 1); 

这里要注意的是,重定向中的拷贝,不是将文件描述符表中的下标进行拷贝,而是对下标处的内容(文件结构体指针)进行拷贝!

使用dup2在打开文件log.txt后,进行了输出重定向,将原本输出到显示器的内容写入到了log.txt文件中。再次更改代码open的宏参数(O_TRUNC -> O_APPEND),就成为了追加重定向操作。结果如下所示:
在这里插入图片描述
同样的,可以修改代码让其重定向标准输入流至文件(默认read从stdin文件读数据,重定向后,从log.txt文件中读)。这一过程称为输入重定向

在这里插入图片描述

以上是使用dup2重定向系统调用函数write、read,前面提到c语言printf、fprintf底层也是这样的文件描述符表的结构,那是否可以控制c语言中的输入输出呢?

dup2(fd,1);
printf("hello printf\n");
fprintf(stdout,"hello printf\n");

回想之前的章节介绍到echo指令,可以进行输出重定向,cat指令可以进行输入重定向

echo "hello" > log.txt   //输出重定向
cat < log.txt            //输入重定向
echo "hello" >> log.txt  //追加重定向

进程的替换不会影响文件的访问(包括重定向操作)——复习进程替换

stdout与stderr都是可以向显示器打印,为什么要有2?他们俩的区别是什么?

有如下代码,表示将字符串分别输出到1(标准输出流)和2(标准错误流)中。
在这里插入图片描述

$ ./mytest 1>normal.log 2>err.log
//将stdout的数据重定向至normal.log
//将stderr的数据重定向至err.log

在这里插入图片描述

实际上,1和2是相同的实现方式,只不过在使用中,相较于正常结果而言,更关注的是它的错误信息,而正常运行的信息往往很多,不便错误的筛查与纠正。因此,为了将错误信息分离出来,才有了标准错误流。

一个衍生用法:

$ ./mytest >normal.log 2>&1

再谈“一切皆文件”

1. 外设设备与文件系统的关系

在这之前我们知道:所有操作计算机的动作都是由进程执行的,包括文件的访问,每一种外设都要有描述他们的结构体对象(struct_dev)

此外,每一种外设都有其相独特的读写方法,纵然每个外设对应的访问实现方式不同(各家外设设备驱动的不同),而对于操作系统来看,这些外设无非都是一些需要进行读写的文件,而能够直接进行文件访问读写的就是进程(open接口),打开新的文件就会创建一个新的struct_file,这个结构体是不是很熟悉?在这个结构体中,就存在着能够指向该文件具体实现自身读写行为的指针(struct fils_operations*),例如(指向了不同磁盘的读写方法,不同键盘的读写方法)。

  • 在Linux中,将struct_file这一层的逻辑关系称为虚拟文件系统(VFS)

外设差异化被封装在驱动中:不同厂商的驱动实现自己的读写逻辑(如razer_keyboard_readlogitech_keyboard_read),但必须遵循操作系统定义的接口。
操作系统通过抽象层统一接口:上层应用只需调用 read()、write() 等标准接口,无需关心底层是罗技还是雷蛇设备。

2. 扩展思想:

这种设计模式与 面向对象编程中的多态性高度相似

​基类(抽象接口) ​:操作系统定义的设备驱动接口(如file_operations)。
​派生类(具体实现) ​:厂商驱动的读写函数(如雷蛇、罗技的实现)。
运行时多态 :通过函数指针动态绑定到具体实现。

通过这种机制,操作系统实现了外设的 ​​“高内聚、低耦合”​,使得硬件厂商可以自由创新,同时保持软件生态的兼容性。


总结

👍 ​感谢各位大佬观看。如果本文有帮助,请点赞收藏支持~

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

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

相关文章

基于尚硅谷FreeRTOS视频笔记——15—系统配制文件说明与数据规范

目录 配置函数 INCLUDE函数 config函数 数据类型 命名规范 函数与宏 配置函数 官网上可以查找 最核心的就是 config和INCLUDE INCLUDE函数 这些就是裁剪的函数 它们使用一个ifndef。如果定义了&#xff0c;就如果定义了这个宏定义&#xff0c;那么代码就生效。 通过ifn…

Linux网络编程 深入解析TFTP协议:基于UDP的文件传输实战

知识点1【TFTP的概述】 学习通信的基本&#xff1a;通信协议&#xff08;具体发送上面样的报文&#xff09;、通信流程&#xff08;按照什么步骤发送&#xff09; 1、TFTP的概述 tftp&#xff1a;简单文件传输协议&#xff0c;**基于UDP&#xff0c;**不进行用户有效性验证 …

c# MES生产进度看板,报警看板 热流道行业可用实时看生产进度

MES生产进度看板&#xff0c;报警看板 热流道行业可用实时看生产进度 背景 本软件是给宁波热流道行业客户开发的生产电子看板软件系统 功能 1.录入工艺流程图&#xff08;途程图&#xff09;由多个站别组成。可以手动设置每个工艺站点完成百分比。 2.可以看生成到哪个工…

初识Redis · C++客户端string

目录 前言&#xff1a; string的API使用 set get&#xff1a; expire: NX XX: mset,mget&#xff1a; getrange setrange: incr decr 前言&#xff1a; 在前文&#xff0c;我们已经学习了Redis的定制化客户端怎么来的&#xff0c;以及如何配置好Redis定制化客户端&…

华硕原厂系统枪神9/9p超竟版-WIN11原装开箱出厂系统安装

华硕原厂系统枪神9/9p超竟版-WIN11-24H2-专业工作站版本安装可带F12-ASUSRecovery恢复功能 适用机型&#xff1a; G635LX、G635LW、G835LX、G835LW、G615LW、G615LP、G615LM、G615LH G815LW、G815LP、G815LM、G815LH、G635LR、G835LR、G615LR、G815LR 远程恢复安装&#xff…

CF1016赛后总结

文章目录 前言T1:Ideal GeneratorT2&#xff1a;Expensive NumberT3:Simple RepetitionT4&#xff1a;Skibidi TableT5:Min Max MEXT6:Hackers and Neural NetworksT7:Shorten the Array 前言 由于最近在半期考试&#xff0c;更新稍微晚了一点&#xff0c;还望大家见谅 &#…

QT聊天项目DAY06

1.从git上同步项目 编译测试&#xff0c;编译通过 Post请求测试 测试成功 2. email is 打印有问题&#xff0c;检查 解析结果是存储在jsonResult中的&#xff0c;修改 3. 客户端实现Post验证码请求 3.1 同步Qt客户端项目 检查QT版本&#xff0c;由于我在公司用的还是QT5.12.9…

GNU,GDB,GCC,G++是什么?与其他编译器又有什么关系?

文章目录 前言1. GNU和他的工具1.1 gcc与g1.2 gdb 2.Windows的Mingw/MSVC3.LLVM的clang/clang4.Make/CMake 前言 在开始之前我们先放一段Hello World&#xff1a;hello.c #include <stdio.h>int main() {printf("Hello World");return 0; }然后就是一段老生常…

笔记整理五

STP生成树 stp生成树是用于解决二层环路问题的协议。 二层环路为有以下三种&#xff1a; 1.广播风暴 2.MAC地址的偏移&#xff08;每一次循环&#xff0c;都会导致交换机来回刷新MAC地址表记录&#xff09; 3.多帧复制 stp生成树&#xff1a;需要将原本的环型拓扑结构转换…

奥比中光tof相机开发学习笔记

针对奥比中光 tof相机&#xff0c;官方提供的资料如下ProcessOn Mindmap|思维导图 Orbbec SDK Python Wrapper基于Orbbec SDK进行设计封装&#xff0c;主要实现数据流接收&#xff0c;设备指令控制。下面就其开发适配进行如下总结&#xff1a; &#xff08;1&#xff09;系统配…

【面试向】点积与注意力机制,逐步编码理解自注意力机制

点积&#xff08;dot product&#xff09;两个向量点积的数学公式点积&#xff08;dot product&#xff09;与 Attention 注意力机制&#xff08;Attention&#xff09;注意力机制的核心思想注意力机制中的缩放点积自注意力机制中&#xff0c;谁注意谁&#xff1f; 逐步编码理解…

一个 CTO 的深度思考

今天和一些同事聊了一会&#xff0c;以下是我的观点 我的观点&#xff0c;成年人只能筛选&#xff0c;不能培养在组织中&#xff0c;应该永远向有结果的人看齐。不能当他站出来讲话的时候&#xff0c;大家还要讨论讨论&#xff0c;他虽然拿到结果了&#xff0c;但是他就是有一…

SQL通用语法和注释,SQL语句分类(DDL,DML,DQL,DCL)及案例

目录 SQL通用语法和注释 SQL语句分类&#xff08;DDL&#xff0c;DML&#xff0c;DQL&#xff0c;DCL&#xff0c;TPL&#xff0c;CCL&#xff09; DDL&#xff08;数据定义语言&#xff09; 数据库操作 查询&#xff08;SHOW、SELECT&#xff09; 创建&#xff08;CREAT…

AUTOSAR图解==>AUTOSAR_SWS_KeyManager

AUTOSAR KeyManager详细分析 AUTOSAR 4.4.0 版本密钥与证书管理模块技术分析 目录 1. 概述2. KeyManager架构 2.1 KeyManager在AUTOSAR架构中的位置2.2 架构说明 3. KeyManager模块结构 3.1 模块组件详解3.2 配置项说明 4. KeyManager证书验证流程 4.1 证书验证流程分析 5. Ke…

Jsp技术入门指南【七】JSP动作讲解

Jsp技术入门指南【七】JSP动作讲解 前言一、什么是JSP动作&#xff1f;二、核心JSP动作详解1. jsp:include&#xff1a;动态包含其他页面与<% include %>的区别 2. jsp:forward&#xff1a;请求转发到另一个页面3. jsp:param&#xff1a;为动作传递参数4. jsp:useBean&am…

10软件测试需求分析案例-查询学习信息

用户登录系统后&#xff0c;进入查询学生信息界面&#xff0c;输入查询字段值&#xff0c;点击查询按钮后&#xff0c;展示查询到的学生信息&#xff0c;可以重新输入字段值进行查询。 查询学生信息属于学生信息管理的子菜单&#xff0c;可以根据学号、姓名、性别查询。老师登录…

基于尚硅谷FreeRTOS视频笔记——6—滴答时钟—上下文切换

FreeRTOS滴答 FreeRTOS需要有一个时钟参照&#xff0c;并且这个时钟不会被轻易打断&#xff0c;所以最好选择systick 为什么需要时间参照 就是在高优先级任务进入阻塞态后&#xff0c;也可以理解为进入delay&#xff08;&#xff09;函数后&#xff0c;需要有一个时间参照&…

MCP服务,阿里云百炼,Cline,mysql-mcp-server,MCP通信原理

简介 MCP&#xff08;Model Context Protocol&#xff09;&#xff0c;模型上下文协议&#xff0c;是一种开放标准&#xff0c;用于将AI模型与外部数据源和工具建立安全的双向连接&#xff0c;它就像AI领域的USB-C接口&#xff0c;为AI模型提供了一种标准化方式来连接不同的数…

一个项目中多个Composer的使用方法

composer是依赖管理工具。 有时我们会在一个项目中使用到多个composer&#xff0c;且每个版本不同。 前提&#xff1a;例如项目xyz根目录vendor中存在阿里云的对应代码。我现在需要再composer腾讯云短信发送的SDK。 1、随便找个位置新建文件夹&#xff0c;存储腾讯云短信发送…

MCP 应用案例-网络设备批量管理

案例背景 需求痛点 企业需管理数百台跨地域网络设备&#xff08;交换机/路由器&#xff09;&#xff0c;传统方式存在&#xff1a; 人工SSH登录效率低脚本维护成本高&#xff08;不同厂商CLI语法差异&#xff09;状态监控依赖独立监控系统 解决方案 通过MCP协议构建智能网络…