Linux|内存级文件原理

news2025/3/1 8:07:02

目录

 进程与文件

Linux下的文件系统

 文件操作,及文件流

 C语言函数

 文件流

 文件描述符        

 系统调用操作

系统调用参数

重定向与文件描述符

输出重定向

输入重定向 


文件=内容+属性

Linux下一切皆文件


 进程与文件

         当我们对文件进行操作时,文件必须要被加载到内存中,然后CUP从内存中拿到此文件进行操作,没有打开的文件放在磁盘中存储。

        文件的打开其实也是设计到内部某个进程。无论是系统调用,还是专有库中的函数,都是启动进程来进行打开。进程会自动记录目前启动时的当前路径,平常所说的相对路径就是指相对于当前进程路径下的路径。当我们没有特意说明文件路径在此进程中对文件操作时,默认会在此进程的路径下进行。比如我们使用C语言新建文件使用绝对路径,默认就会在此进程的路径下进行,若是此进程的路径发生改变,新建的文件会在改变后的路径下进行,这就是相对路径的原理

        一个进程可以打开多个文件,那么我们知道系统中存在多个进程,系统中必然存在很多被进程打开的文件,OS是一点要管理多个被打开的文件的,如何管理呢,同样是先描述在组织,所以呢内核中一定要有描述被打开文件的结构体,并定义其对象!被打开的文件叫做内存级文件,这也是这篇文章的重点,没打开的我们后续再说,现在我们逐步解开描述被打开文件的结构体的面纱。


Linux下的文件系统

         在Linux系统下,我们可以把一切都看成文件(包括硬件)。Linux系统有一个重要特性,即“一切皆文件”的原则。无论是普通的磁盘文件、目录,还是网络套接字、硬件设备,在Linux中都被抽象为文件。

        我们平常使用高级语言进行底层文件的调用,本质上是封装了系统调用。因为用户不能直接调用系统硬件,本质上是操作系统进行调用的。我们通常使用语言进行调用实际就是操作系统提供了相应的接口供用户使用。比如以C语言为例,C语言的库函数接口fopen、fclose、fread、fwrite 在某种意义上来讲调用的是系统接口open、close、read、write(这些函数运用跟C中的文件操作相似,可用man指令查看文档),只不过对系统调用进行了封装。系统调用接口和库函数的关系如下:

 这里先对linux下一切皆是文件混个眼熟,后面会解释具体原因!!!

 文件操作,及文件流

 C语言函数

fwrite

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

返回值:写入了多少个基本单位

用于将二进制数据写入文件

fgets

char *fgets(char *str, int n, FILE *stream);

返回值

成功时:返回指向存储数据的指针 str。

失败或遇到文件结束(EOF):返回 NULL。

fopen

FILE *fopen(const char *filename, const char *mode);

返回值

成功时:返回一个指向文件的 FILE 类型指针。

失败时:返回 NULL,并可通过 perror 或 strerror(errno) 获取错误信息。

fputs

int fputs(const char *str, FILE *stream);

返回值

成功:返回非负值(通常为 0)。

失败:返回 EOF(通常为 -1),表示发生错误。

         这几个函数大家可能都用过,但对于FILE*stream是什么,理解的可能就不透彻了,我们先继续往下看

 文件流

         下面我们来认识一下文件流操作。程序在启动时,默认会打开三个文件流:stdin、stdout、stderr。这三种流的类型都是文件指针FILE*。

  1. stdin:标准输入——默认是键盘设备。计算机系统从此文件流中获取数据信息,即从此文件中读取数据。
  2. stdout:标准输出——默认是显示器设备。将数据输出到此文件流中,即从此文件中输出数据。
  3. stderr:标准错误——默认是显示器设备。用于输出程序或命令的错误信息,与stdout原理相似。

 

         正因有了标准输入输出流操作(I/O设备操作),才能使得程序能够与用户和其他程序进行有效的交互。

        那么问题来了,系统下的所有都是文件,程序系统又是如何找到对应的文件?

        其实每个文件都有一个对应的文件描述符进行标志。文件描述符是一个非负整数,与文件名形成了一种索引关系,使得程序可以通过这个整数来访问和操作对应的文件。

 文件描述符        

        文件描述符的范围是0到N,其中0、1、2是特殊文件的文件描述符:0代表标准输入(stdin),1代表标准输出(stdout),2代表标准错误输出(stderr)。一般情况下,文件描述符从3开始数往后分配。因为内部的文件描述符其实就是存放管理文件结构体(struct file:包含三个方面,第一个是能够通过指针让我们找到文件的属性,第二个是对文件操作的一堆方法,第三个是是所提供的缓冲区。打开一个文件系统内部就会创建一个struct file结构体对文件进行管理)的指针数组 fd_array 的下标,此指针数组每个元素都是一个指向打开文件的结构体指针,而task_struct内部存在一个指针,指向存放此指针数组的结构体(struct files_struct)。

        总的来说文件描述符就是数组的下标,当使用一个文件时就必须找到此文件的文件描述符,通过文件描述符来找到对应的文件。这里的重点在于文件操作符,只要我们拿到文件操作符fd,就能够通过file_struct结构体内部的fd_array数组指针找到对应管理文件的结构体file,对其文件进行操作。

        所以,C标准库中的FILE(文件流:随机读取或写入文件,即与文件操作的底层)其实就是自己封装的一个结构体,里面封装了 stdin、stdout、stderr 的文件描述符0,1,2。之所以系统不直接封装而让语言单独封装是为了保证可移植性。若是系统直接封装,一旦换了平台系统可能就会出问题,导致不可移植。其实不仅仅是流操作,很多有关系统接口也一样,为了保证可移植性,都是在不同语言内部封装不同系统调用的接口和相关的文件接口。

系统调用操作

         既然已经知道了文件描述符,我们进一步来了解系统调用。上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。而, open close read write lseek 都属于系统提供的接口,为系统调用接口。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

用open打开文件,当文件不存在新建文件时,需要用到第三个参数,设置文件权限,

pathname: 要打开或创建的目标文件

flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。

系统调用参数

 O_RDONLY: 只读打开

 O_WRONLY: 只写打开

 O_RDWR : 读,写打开

 以上三个常量,必须指定一个且只能指定一个

O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限

O_APPEND: 追加写

注意:Mode给定权限,权限比如给0666但是为了不被初始0002影响,设置一个umask(0)掩码写在文件中,就会按照我们给的权限直接设置了

 int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);使用open时,我们传递系统调用参数时候用到了|,这代表按位或运算,为什么这么传参数呢,我们来具体分析:

        倘若你参数是一个变量,那么多个参数就代表多个变量需要传递,这是一个有点浪费空间的举动,因为一个变量就是多个字节;对于参数来说它只代表一个功能,如果你传递了这个参数就代表希望其有这个功能,其实就是有和没有的区别,那你完全可以用1,0这样的数字来传;进一步节省空间,我们可以利用位图的思维来处理这个事情,及按照bit位的1,0来代表有某个参数或没有,在说的直白些就是有某个功能或没有。我们提前对每个bit位约定好如果是1代表具有哪个功能属性,对最后所有的参数取或操作成为一个集中的参数,转换为2进制,位置上有1就就代表有这个参数的传递,在使用的时候用约定好功能绑定的那个比特位和传进来的参数取异为1就执行这个功能;或起来的整体不就可以用很小的内容传递多个参数了,大大节省空间,提升效率!!!!

下边这个图片解释了,用bite位传递参数,并且使用的的实现例子

1<<0= 00001 等价于 1

1<<1=00010 等价于 2

1<<2==00100等价于 4

以此类推

1<<n 表示将数字 1 左移 n 位。

&:

都为 1 时,结果为 1。

只要有一个为 0,结果为 0。

If中只要不是0都可以输出

 linux下一切皆是文件

        现在我们来理解这句话,现在我们来理解这句话,linux下一切皆文件,更准确的说是linux下一切皆struct file,屏蔽底层差异。如何理解呢,先看下面这个图片

        我们将键盘,显示器,磁盘,网卡都描述为stuct,他们都有系统对应停供的系统接口,读操作,写操作,可是键盘没有写,显示器没有写,磁盘的读写也不会和网卡一模一样,但是我们调用的接口都是一样的!!!

         这其实就是上层调用读写任何设备,使用文件对象的函数,指针调用方法,屏蔽硬件底层差异,上层使用统一的接口。所以linux下一切都可以是文件对象,根据各部分实际的不同再来执行不同的操作。对于linux下一切皆文件可能还是不那么清楚,其实这也只是其中的一个,其他的原因也会随着不断深入学习浮出水面

重定向与文件描述符

输出重定向

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
 close(1);
 int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
 if(fd < 0){
 perror("open");
 return 1;
 }
 printf("fd: %d\n", fd);
 fflush(stdout);

 close(fd);
 exit(0);
} 

        这里关闭了1,也就是stdout:标准输出流,所以这时候我们打开的文件fd就变成了1,默认输出到1,而此时1变成了fd,所以本应该打到显示器上的内容被显示在了文件里。

        如果关闭0就是输入重定向,打开的文件就成了0,所以文件里的内容就会被显示在显示屏上,因为默认从0里面获取输入,本来是键盘,变成了文件而已。

        文件描述符会按照最小下标分配,以上程序中关掉了系统下标准输出流的文件描述符1。至于fileno(stdout)的调用,由于stdout是C语言的流,非系统专属,当程序启动时C的stdout就默认打开,因此close(1);只是关闭了与stdout关联的文件描述符,与C的stdout内部文件数据没有关系,fileno(stdout)仍会返回原始的文件描述符值(即1),但是 close是系统操作,它会关闭了底层系统文件描述符表索引值1所指向的 stdout 文件。当我们创建文件log.txt时系统会给此文件分配描述符1。C中的输出都是往文件描述符为1所对应的文件中输出的,即一般情况下都是往标准输出流stdout中输出。这里log.txt的文件描述符为1,进程拿到文件描述符后会自动往文件描述符表fd_array中寻找索引值为1对应的文件中输出,所以就会出现以上输出重定向。输入重定向同理,将文件描述符为0的进行重新指向。

输入重定向 

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

int main()
{
    close(0);   //将标准输入重定向(键盘)去除
    open("log.txt", O_RDONLY);   //此时文件log.txt的文件描述符为0,即成为了输入流
    int a = 0;
    cin >> a;  //从输入流中读取数据
    cout << a << endl;  
    return 0;
}

输出:123456

         不难发现,以上类似的程序完成重定向功能比较麻烦——先close关闭再open分配。说白了,重定向功能就是分配到指定的文件描述符,而文件描述符对应指定文件的功能,这里我们可直接让指定文件的文件描述符指向对应功能的文件描述符所指向文件的功能即可。比如将文件描述符为3的指向文件描述符为1所对应的系统文件,即指针数组元素之间的浅拷贝fd_array[1]=fd_array[3]。

#include <unistd.h>

int dup2(int oldfd, int newfd);

 

        注意,这里的oldfd会保留到最后,所以oldfd是我们想重定向的文件名,newfd就是我们想替换的文件名1、2之类的。 

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

int main()
{
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    dup2(fd, 1);
    cout << "fd = " << fd << endl;
    return 0;
}

输出:fd = 3

 补充命令:

 进程在启动的时候会记录的路径是当前路径 命令:cwd,chdir:更改路径

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

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

相关文章

40分钟学 Go 语言高并发:Context包与并发控制

Context包与并发控制 学习目标 知识点掌握程度应用场景context原理深入理解实现机制并发控制和请求链路追踪超时控制掌握超时设置和处理API请求超时、任务限时控制取消信号传播理解取消机制和传播链优雅退出、资源释放context最佳实践掌握使用规范和技巧工程实践中的常见场景…

【SpringMVC - 1】基本介绍+快速入门+图文解析SpringMVC执行流程

目录 1.Spring MVC的基本介绍 2.大致分析SpringMVC工作流程 3.SpringMVC的快速入门 首先大家先自行配置一个Tomcat 文件的配置 配置 WEB-INF/web.xml 创建web/login.jsp 创建com.ygd.web.UserServlet控制类 创建src下的applicationContext.xml文件 重点的注意事项和说明…

neo4j图数据库community-5.50创建多个数据库————————————————

1.找到neo4J中的conf文件&#xff0c;我的路径是&#xff1a;D:\Program Files\neo4j-community-5.5.0-windows\neo4j-community-5.5.0\conf 这里找自己的安装路径&#xff0c; 2.用管理员模式打开conf文件&#xff0c;右键管理员&#xff0c;记事本或者not 3.选中的一行新建一…

如何最简单、通俗地理解Python的迭代器?

我们知道迭代器&#xff08;iterator&#xff09;可以用for循环去取数&#xff0c;这和列表取数有什么区别呢&#xff1f; 想理解Python迭起器的差异&#xff0c;有个很简单的例子 打个比方&#xff0c;你去玩街头投篮机&#xff0c;可以投5个球&#xff0c;这里有两种方式&a…

JavaEE 【知识改变命运】02 多线程(1)

文章目录 线程是什么&#xff1f;1.1概念1.1.1 线程是什么&#xff1f;1.1.2 为什么要有线程1.1.3 进程和线程的区别1.1.4 思考&#xff1a;执行一个任务&#xff0c;是不是创建的线程或者越多是不是越好&#xff1f;&#xff08;比如吃包子比赛&#xff09;1.1.5 ) Java 的线程…

Linux内核USB2.0驱动框架分析--USB包

一&#xff0c; 包的组成 每个包都由SOP&#xff08;包起始域&#xff09;、SYNC&#xff08;同步域&#xff09;、Packet Content&#xff08;包内容&#xff09;、EOP&#xff08;包结束域&#xff09;四部分组成&#xff0c;其中SOP、SYNC、EOP为所有包共有的域&#xff0c…

云轴科技ZStack亮相2024 IDC中国生态峰会,共塑AI时代IT生态新格局

11月21日&#xff0c;2024 IDC中国生态峰会在北京举办&#xff0c;吸引了超过300位生态伙伴齐聚一堂&#xff0c;聚焦行业内最前沿的热点话题。本届峰会以“创见先机&#xff0c;智领风云”为主题&#xff0c;深入探讨宏观经济趋势、技术革新以及如何融合AI与数据技术&#xff…

C0029.在Clion中解决Debug时,提示Process finished with exit code -1的错误

1.错误提示 Process finished with exit code -12.解决办法 如上在使用Debug进行代码调试时&#xff0c;直接出现如上报错&#xff0c;解决办法就是直接点击运行程序&#xff0c;即可查出报错编号&#xff0c;然后根据报错编号来查找问题&#xff1b; 然后在网上就可以根据该…

07-Making a Bar Chart with D3.js and SVG

课程链接 Curran的课程&#xff0c;通过 D3.js 的 scaleLinear, max, scaleBand, axisLeft, axisBottom&#xff0c;根据 .csv 文件生成一个横向柱状图。 【注】如果想造csv数据&#xff0c;可以使用通义千问&#xff0c;关于LinearScale与BandScale不懂的地方也可以在通义千…

读取各种来源格式单细胞数据集构建seurat分析对象,代做生信分析

参考资料和分析注意事项 全流程的分析指导视频 演示数据集网盘文件 分析参数文件路径格式的特别提示 大家给要分析用到的文件路径或目录路径的时候&#xff0c;以D:/omics_tools/demo_data/scrnaseq/GSE189125/GSE189125_5prime_scRNAseq_seqbatchA_counts.txt.gz 这个文件为…

SQL-多表操作

前文所介绍的sql操作都是基于单表进行的&#xff0c;接下来我们来学习多表操作。 多表设计 在实际的项目开发中&#xff0c;会根据业务需求和业务模块之间的关系进行数据库表结构设计&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xf…

c++ STL线程安全使用

c STL不是线程安全的&#xff0c;因此在多线程中使用的时候&#xff0c;操作同一个容器&#xff0c;会崩溃&#xff0c;因此需要解决线程安全的问题&#xff1a; 使用实例类似于以下&#xff1a; #include <thread> #include <vector> #include "thread_safe…

Swift 实现判断链表是否存在环:快慢指针法

文章目录 前言摘要描述题解答案题解代码题解代码分析示例测试及结果时间复杂度空间复杂度总结关于我们 前言 本题由于没有合适答案为以往遗留问题&#xff0c;最近有时间将以往遗留问题一一完善。 LeetCode - #141 环形链表 不积跬步&#xff0c;无以至千里&#xff1b;不积小流…

SpringCloud实用-OpenFeign 调用三方接口

文章目录 前言正文一、项目环境二、项目结构2.1 包的含义2.2 代理的场景 三、完整代码示例3.1 定义FeignClient3.2 定义拦截器3.3 配置类3.4 okhttp配置3.5 响应体3.5.1 天行基础响应3.5.2 热点新闻响应 3.6 代理类3.6.1 代理工厂3.6.2 代理客户端3.6.3 FeignClient的建造器 四…

C++设计模式行为模式———中介者模式

文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式&#xff0c; 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互&#xff0c; 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…

HarmonyOS:使用ArkWeb构建页面

一、简介 页面加载是Web组件的基本功能。根据页面加载数据来源可以分为三种常用场景&#xff0c;包括加载网络页面、加载本地页面、加载HTML格式的富文本数据。 页面加载过程中&#xff0c;若涉及网络资源获取&#xff0c;需要配置ohos.permission.INTERNET网络访问权限。 二、…

矩阵的拼接

矩阵的拼接分为横向拼接和纵向拼接 注意&#xff1a;横向拼接要求两矩阵行数相同&#xff0c;纵向拼接要求两矩阵列数相同 h o r z c a t horzcat horzcat和 v e r t c a t vertcat vertcat函数 h o r z c a t ( a , b ) horzcat(a,b) horzcat(a,b)将 a a a和 b b b横向拼接&a…

SpringCloud框架学习(第五部分:SpringCloud Alibaba入门和 nacos)

目录 十二、SpringCloud Alibaba入门简介 1. 基本介绍 2.作用 3.版本选型 十三、 SpringCloud Alibaba Nacos服务注册和配置中心 1.简介 2.各种注册中心比较 3.下载安装 4.Nacos Discovery服务注册中心 &#xff08;1&#xff09; 基于 Nacos 的服务提供者 &#xf…

Ollama vs VLLM:大模型推理性能全面测评!

最近在用本地大模型跑实验&#xff0c;一开始选择了ollama,分别部署了Qwen2.5-14B和Qwen2.5-32B&#xff0c;发现最后跑出来的实验效果很差&#xff0c;一开始一直以为prompt的问题&#xff0c;尝试了不同的prompt&#xff0c;最后效果还是一直不好。随后尝试了vllm部署Qwen2.5…

.NET9 - 新功能体验(一)

被微软形容为“迄今为止最高效、最现代、最安全、最智能、性能最高的.NET版本”——.NET 9已经发布有一周了&#xff0c;今天想和大家一起体验一下新功能。 此次.NET 9在性能、安全性和功能等方面进行了大量改进&#xff0c;包含了数千项的修改&#xff0c;今天主要和大家一起体…