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

Linux文件理解,基础IO理解

目录

1.理解文件

        狭义理解文件:

        广义理解文件:

        对文件操作的理解:

        从系统角度看文件:

2.常用的C接口

3.今天我们想把信息打印到显示器,我们可以怎么做呢?


1.理解文件

        1.我们知道,文件=内容+属性,我们对文件作操作无非就是对内容作操作,要么是对属性作操作。当我们创建一个文件,我们的文件没有内容,那么这个文件占不占空间,当然占,因为虽然内容不占空间但是文件的属性要占文件,如果没有属性,那么这个文件到底存不存在呢?

        2.要想访问一个文件,就要先打开一个文件。

        3.如果一个文件就没有被打开,那么这个文件就存储在磁盘上。

        4.那么问题是?文件是被谁打开的呢?文件是被进程打开的。

        5.我们可以推断,我们的OS内一定存在大量被打开的文件。那么操作系统要不要管理这些文件呢?要!怎么管理?先描述,再组织。

        我们打开文件,本质就是进程打开文件,把文件从磁盘加载到内存里进行访问,因为根据冯若依曼,CPU无法直接访问磁盘等外设,CPU只和内存打交道。

        那么我们没有被打开的文件在哪里呢?在磁盘,没有被打开的文件要不要被管理呢?要!怎么管理?先描述,再组织。

        6.进程有task_struct,文件被进程打开,我们研究被打开的文件,本质就是研究进程和文件之间的关系!

        狭义理解文件:

⽂件在磁盘⾥
磁盘是永久性存储介质,因此⽂件在磁盘上的存储是永久性的
磁盘是外设(即是输出设备也是输⼊设备)
磁盘上的⽂件 本质是对⽂件的所有操作,都是对外设的输⼊和输出 简称 IO

        广义理解文件:

Linux 下⼀切皆⽂件(键盘、显⽰器、⽹卡、磁盘…… 这些都是抽象化的过程)(后⾯会讲如何去
理解)

        对文件操作的理解:

对于0KB的文件是占空间的,文件是文件属性和文件内容的集合,所有文件操作的本质是文件内容操作和文件属性操作。

        从系统角度看文件:

        对文件的操作本质是进程对文件的操作。磁盘的管理者是操作系统,因为操作系统是管理软硬件资源的软件大管家。文件的读写本质不是通过我们的库函数进行的,我们对内核做操作,底层必定封装了系统调用,我们的系统调用才可以对内核做操作,所以我们读写对底层做操作一定是通过系统实现的。

我们发现我们打开一个文件需要知道文件的路径。

结论:打开文件,必须先找到文件,要找到文件,就必须知道文件的路径和文件名,这就是为什么要有cwd的原因之一

2.常用的C接口

        当我们想知道我们的进程信息我们可以, ls  /proc/pid -l

        首先我们可以打开文件用fopen,filename是文件名,mode是权限。
FILE *fopen(const char *filename, const char *mode);

3.今天我们想把信息打印到显示器,我们可以怎么做呢?

        首先我们先理解一下,我们的printf当我们把一个int打印到屏幕上。它真的是整型吗?NO,它是把整型转字符串进行打印的,类似的我们从键盘输入给int,我们输入的真的是Int吗?NO,输入的是字符串,只不过给转成整型了,现在我们想一想我们初学C语言时我们说printf和sacnf是格式化输入输出不就是这个意思吗?

        所以我们的显示器和键盘叫做字符设备,因为它们输入和输出的都是字符。转化工作是函数来做的。

我们知道读写文件要有文件的位置才可以进行读写,那么我们进一步理解一下文件,文件的读写位置其实就是文件在数组中存储的下标啊!

        你有没有想过为什么我们printf是往屏幕上打印的?根据我们所说Linux一切皆文件,那么我们打印到屏幕上应该是往屏幕文件里打印啊,其实当我们每个进程打开的时候会默认打开三个输入输出流,也就是3个文件,一个是stdin,一个是stdout,一个是stderr。

顾名思义,stdin是输入流,stdout是输出流,err是错误流,打印我们的错误信息的。

我们本质向显示器打印,就是向stdout文件进行写入,因为stdout也是FILE*

我们知道我们的fopen是我们的C语言库函数,库函数它想打开文件也必须调用系统调用才可以,实际上,库并不被操作系统信任!那么我们也可以使用系统调用来打开我们的文件。我们的open一个是pathname,一个是flags。

我们的flags有好几种,它们之间可以进行组合达成不同的效果,读写或者覆盖读写,文件不存在创造?

        我们的Mode是我们的权限,用umask来设定,我们一般设定为0666。

我们的C标准库,C++标准库,JAVA,python都是语言,是在我们操作系统上方的,但是他们想要打开文件都需要调用操作系统给它们提供的接口,每个语言调用系统调用的方式都不同,但是我们只需要知道我们的系统调用是怎么做到的,我们就可以对操作系统进行操作了,避开了语言给我们提供的标准库。

那么我们的C语言为什么要封装文件操作接口呢?一是系统调用麻烦(感觉不麻烦啊!),最重要的就是我们的语言跨平台性!!!这个可以大幅增强语言的竞争力,试想如果我们的C语言不在mac上做适配,那么所有使用mac操作系统的人都无法使用C语言,市场竞争力下降,逐渐淘汰,市面上主流的语言现在一定是多平台适配的,不然它就会丧失使用这个操作系统的人啊!!!这是最关键的!!!所以我们语言更新换代慢就是因为它需要对所有的操作系统做适配!

        

我们惊讶的发现,我们的open返回值竟然是一个int,这是怎么回事???

我们的打印我们open返回的一些值我们发现它是3,4,5,6,。依次递增!!!

那么0去哪里了?答案是我们忘记了吗?我们每个进程启动默认打开3个文件啊,stdin,stdout,stderr。

还是回归到我们上面的那个问题,我们通过ls -l /proc 

发现我们的操作系统存在大量的进程,意味着我们的操作系统也存在被大量打开的文件,我们的操作系统要不要对这些被打开的文件进行管理呢?当然要,怎么管理?先描述,再组织!!!

先给出结论我们的open返回值是数组下标,我们所有在一个进程被打开的文件被存储在一个数组里,我们文件存在这个数组哪个位置我们就返回哪个位置的下标!!!

        我们的操作系统管理进程是通过task-struct结构体进行管理的,而我们的进程之间是通过双链表链接起来的,我们的操作系统对进程管理就转化成了对双链表的增删查改,那么我们的task_struct里面也有file,来管理我们每个进程打开的文件,多个进程可以打开同一个文件,而且它们在各个进程中的下标还不一样!!!

        我们通过struct file来描述我们每个打开的文件,task_struct里面存储了一个指针数组,存储了指向struct file的指针!!!我们打开每个文件就是返回我们这个文件在进程的文件数组里的下标,这样我们进程知道了下标,就可以通过下标找到这个被进程打开的文件,然后我们的文件之间也通过双链表进行链接,这个文件是所有的文件,不是我们一个进程打开的文件哦!!!我们对文件的管理就转化成了对链表的增删查改。我们的file存着,我们文件的属性和内容。

、所以我们来理解一下我们的PCB和我们的文件的关系,我们知道我们进程可以打开文件我们研究打开的文件就是研究进程和打开的文件的关系我们操作系统需要对我们的进程进行管理,管理方法是先用PCB描述进程的属性,通过PCB可以找到进程对应的代码和数据,而我们的被打开的文件也需要进行管理,管理方法是用struct file来对文件的属性进行描述,我们的PCB进程属性里面存着这个一个数组,这个数组是struct file*array[],里面记录着我们当前进程打开的文件,用下标进程就能找到它打开的对应的文件!!!

现在我们想象一下,我们printf无非就是往stdin这个文件进行数据的写入,那么我们之前学过的重定向不就是0_trunc选项,输入覆盖选项,把我们的往屏幕上写的内容写到文件里不就是让这个文件占据着1号下标的位置,让上层以为它还是往屏幕上写入,实际上我们已经完成了重定向吗?

ps:我们博客的讲解会串联以往的知识,让我们的知识从边缘不断拓展,这样可以帮助我们复习和理解,实际上我们的Linux操作系统都是环环相扣的,虽然我们不会写操作系统(毕竟也没几个人能写),但至少我们可以理解一下操作系统的底层到底是怎么搞的,认识操作系统,理解操作系统,使用操作系统!!!

http://www.lryc.cn/news/601514.html

相关文章:

  • SCUDATA esProc SPL Enterprise Edition(大数据计算引擎) v20250605 中文免费版
  • Keepalive高可用集群的实验项目
  • 【Java系统接口幂等性解决实操】
  • DeepSeek实战--无头浏览器抓取技术
  • Java常用日志框架介绍
  • 五度标调法调域统计分析工具
  • 设计模式(五)创建型:原型模式详解
  • [spring6: Mvc-异步请求]-源码分析
  • 设计模式(三)创建型:抽象工厂模式详解
  • 微服务架构面试题
  • Flutter开发实战之测试驱动开发
  • linux根据pid获取服务目录
  • Gradio.NET 中文快速入门与用法说明
  • IIS发布.NET9 API 常见报错汇总
  • 从 .NET Framework 到 .NET 8:跨平台融合史诗与生态演进全景
  • 9-大语言模型—Transformer 核心:多头注意力的 10 步拆解与可视化理解
  • 电商项目_核心业务_数据归档
  • Java枚举类enum;记录类Record;密封类Sealed、permits
  • Java面试宝典:MySQL执行原理一
  • 300.最长递增子序列,674. 最长连续递增序列,
  • Ubuntu服务器安装与运维手册——操作纯享版
  • 负载均衡Haproxy
  • [AI8051U入门第十一步]W5500-服务端
  • 嵌入式学习日志————对射式红外传感器计次
  • 【MySQL篇】:MySQL基础了解以及库和表的相关操作
  • DP之背包基础
  • SignalR 全解析:核心原理、适用场景与 Vue + .NET Core 实战
  • ASP.NET Core 高并发万字攻防战:架构设计、性能优化与生产实践
  • 一个MySQL的数据表最多能够存多少的数据?
  • 迷宫生成与路径搜索(A算法可视化)