Linux——进程中被打开的文件

news2024/11/15 22:32:25
	C语言中有着许多对文件操作的函数,包括其他语言也有,但是从语言来了解文件有点浅显
	计算机的一切都离不开操作系统,那么文件跟操作系统也有着密切的关系,所以我们从系统
	层面来了解文件(在进程中打开的文件)

文章目录

  • 1.系统中被打开文件的认识
  • 2.C语言中的文件接口
    • a. fopen/fclose
    • b.fwrite/fread
    • c. 部分演示
  • 3. 系统调用接口open/close
    • a. open的参数和返回值
      • path和返回值
      • 标志位
      • mode
    • b. open的返回值fd再认识
    • c. 标准输入输出
    • d. 一切皆文件
  • 4.对文件结构体认识
  • 5. fd的再再认识
    • a. 追加重定向
    • b. 输入重定向
    • c. 问题的引入

1.系统中被打开文件的认识

  我们首先要知道文件 = 文件内容 + 文件属性
  其次为什么要从系统来了解文件?我们使用编程语言写好的程序也能打开文件对文件的数据进行修改。但是我们要知道,文件刚开始是在磁盘里的,语言写好的程序跑起来之后是一个进程,一个进程要打开文件,他能直接打开吗?很显然是不能的,因为文件没被打开前一般是在磁盘里的,而cpu只跟内存打交道,所以要打开一个文件必须要将它加载到内存中,那么谁来管理磁盘跟内存的交互呢?那肯定是操作系统。所以从语言来了解文件是不够本质的。并且从这里也就把文件分成两类:打开的文件和未打开的文件。而我们研究被打开的文件,也就是内存中的文件。
  再想一个问题,一个进程能带开多个文件吗?多个进程能打开多个文件吗?答案是可以的,Linux中万物皆文件,在一个进程跑起来的时候会默认打开三个流:标准输入、标准输出、标准错误。而这三个其中标准输入是键盘,标准输出和标准错误是显示器,而计算机中肯定会有着许多的进程。关于这些详细内容先不细谈,目前只要知道这三个流其实也是文件就ok了,所以一个进程可以打开多个文件,多个进程也可以打开多个文件。进程与打开的文件是1:n的关系
  这么说来,内存中会存在多个被打开的文件,而哪些文件被哪些进程打开了这些都需要被管理,所以文件需要被管理,操作系统管理的方法就是先描述,再组织,所以当一个文件要被打开时,除了要将文件加载到内存中,还要用文件结构体将文件描述好,这个结构体里面有着文件的信息可能有磁盘中的存储位置、文件名等等。然后再用数据结构比如链表将它们管理起来。也说明进程打开文件也是凭借操作系统来打开的,那么操作系统一定要封装系统调用才能让进程打开文件

2.C语言中的文件接口

  在这里我们只认识部分函数

a. fopen/fclose

  这两个是成对出现的,而fclose就是关闭一个文件,我们主要看fopen:
在这里插入图片描述
  它是一个库函数,它的参数第一个是文件的路径(这个路径可以是相对路径也可以是绝对路径),第二个是文件被打开的模式。它的返回值是一个库里封装的FILE的指针,这个指针会指向被打开的文件,如果打开失败会返回空值针。
  我们再看看文件可以有哪几种打开方式:

在这里插入图片描述

r模式:以只读方式打开文件,并且文件被指向的位置是文件内容的开头
r+模式:以读写方式打开问价,并且文件被指向的位置也是文件内容的开头
w模式:以读写的方式打开文件,在打开的时候会先将文件内容清空,然后指向文件内容的开始
w+模式:以读写的方式打开文件,在打开的时候会先将文件内容清空,然后指向文件内容的开始,当文件不存在的时候会创建文件
a模式:以追加(其实也是写)的方式打开文件,但是不会将文件内容清空,而且指向文件内容的末尾,当文件不存在时创建文件
a+模式:以读和追加的方式打开文件,不会清空文件内容,当读文件的时候从文件的开始读,写的时候从文件的末尾开始写。文件不存在时创建文件

b.fwrite/fread

  接下来我将演示打开文件,并对文件进行读写。在此之前我们还要认识一个函数:fread、fwrite
在这里插入图片描述
  这里它们的第一个参数是指向被写入/读的一段空间,第二个参数是每个元素字节大小,第三个是有多少个元素,第四个是要操作的文件。

c. 部分演示

部分演示:

在这里插入图片描述

在这里插入图片描述
  这里我们使用了fopen并且以w+的方式打开文件,刚开始我们这个目录底下是没有文件log.txt的所以要先创建好文件在向文件中写入上面代码中的要写入的内容。下面也是差不多,不过多解释
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
  我们发现我们上面的三个文件打开方式和之后的读写和Linux的重定向好像差不多。

3. 系统调用接口open/close

a. open的参数和返回值

我们上面知道,进程中打开文件,对文件的操作一定离不开操作系统,那么操作系统一定有着封装好的系统调用,来让进程能够对文件进行操作,而上面的进程是C语言编译好的程序,其中C语言中的文件打开对文件的读写肯定也是封装了系统调用。那么我们来认识两个系统调用open和close:

在这里插入图片描述
close不多介绍了就是关闭文件描述符所对应的文件。
在这里插入图片描述

首先是open,可以看到他有两个,这种好像是C++中的重载,怎么会在C语言中呢?其实这只是一种宏替换实现的并不是函数重载。
我们来介绍它的参数和返回值:

path和返回值

返回值:该函数打开文件成功后返回对应的文件描述符fd,失败返回-1
pathname:被打开文件的路径

标志位

后面两个参数我们要着重介绍一下,既然C语言中有着文件打开的方式,俺么系统调用里一定也有,这个就是文件打开的方式,但是它跟C语言中的传的方式不一样,这里使用了位操作的方式传,我们用代码来展示一下位操作传的标志问参数如何使用:
在这里插入图片描述

在这里插入图片描述
这就是标志位的使用,使用一个比特位就能描述一种状态。
这样我们就能介绍这个open中的flag参数怎么使用了,而手册里也告诉我们几种不同的打开方式,这里我们只介绍部分:

O_CREAT:文件不存在的话创建文件。
O_RDONLY:以读方式打开文件。
O_WRONLY:以写的方式打开文件。
O_TRUNC:打卡文件的时候会清空文件内容,并指向文件的开头。
O_APPEND:打开文件会指向文件的末尾。

我们看到这些参数也是全部以横杠+大写的方式命名,说明他们也只是一群宏常数罢了。

mode

接下来是第三个参数,mode:
我们打开一个文件log.txt。不在的时候创建它,在这里我们呢什么都没干,就把他打开后关闭了。
在这里插入图片描述

在这里插入图片描述
当我们看到如上结果的时候,发现文件的权限里怎么会出现S,并且文件是红的,这是因为文件的权限控制其实是乱码,而open中的mode就是控制这一权限的(被进程中创建的文件)。
在这里插入图片描述

在这里插入图片描述
这样就可以了,但是我们又发现,被进程创建好的文件设定的权限和实际的权限不一样,这是因为系统中有着掩码:
在这里插入图片描述
我们也可以在代码中设置进程中进程的掩码:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
但是不推荐这么做

b. open的返回值fd再认识

上面说到,open打开一个文件之后,会返回一个与之对应的文件标识符fd,文件标识符是什么?在C语言中使用fopen打开一个文件之后会返回一个指向被打开文件的FILE的一个指针,它与fd又有什么关系?接下来我们来一一解惑,既然涉及到了文件和进程那就又得从系统开始了解:
我们目前知道被打开的文件会被描述成文件结构体然后用数据结构组织起来(假设链表),那么在一个进程中,可能会需要打开多个文件,那进程和被打开文件又是如何联系起来的呢?其实它们是有着如下图的关系:
在这里插入图片描述
这其中文件如何加载到内存中先不说。
进程pcb中会有一个指针,这个指针指向一个指针数组,这个数组里面存储着被打开文件结构体的指针,这个数组也叫进程文件描述符表,那么这一张表让进程和文件而实现了解耦,就跟进程和内存的页表一样。我们现在来打开一下文件:
在这里插入图片描述
在这里插入图片描述
我们发现,文件标识符是一串连续的整数,这里直接提出结论:文件标识符就是进程文件标识符表的下标,操作系统访问文件就是根据文件标识符来访问
那么C语言中的FILE其实也肯定封装了fd,稍后我们可以进一步验证。
现在我们发现上面的数字只有3、4、5、6。0、1、2呢?我们知道进程在运行的时候会默认打开三个流:标准输入、标准输出、标准错误,那么结果显而易见,012就是这三个。代码说话:
在这里插入图片描述
在这里插入图片描述

那么这张图也说明了012对应的那三个标准输入输出流,FILE结构体里也封装了fd。
那么问题就来了:

为什么要默认打开这三个流?
文件描述符对应着着硬件,一切皆文件到底怎么理解?

c. 标准输入输出

首先来说明第一个问题:
默认打开这三个流,是为了让程序员能够默认进行输入输出的代码的编写(printf,scanf)。

d. 一切皆文件

第二个问题:
我们在使用open,fopen打开文件对文件进行各种操作,然后操作系统定时刷新文件缓存区,将操作得到的文件覆盖到磁盘中的文件中,说白了这些操作不还是和磁盘之间的交互吗?磁盘具有读写的功能,磁盘也是硬件,操作系统会和键盘交互,和网卡、麦克风、屏幕交互。那对于键盘来说该硬件只有读的功能,而屏幕只有写的功能。网卡可以读写,麦克风也只能读。这些功能肯定是在各自驱动中,有着对应的函数。而我们操作系统中的文件结构体中也肯定有着读写的函数指针,创建好一个文件对象的时候,我们让这个文件结构体的读写方法指向硬件的读写方法上:
在这里插入图片描述
每个硬件的读写的实现的方式肯定是不一样的,但是我们都是通过文件结构体对象来统一调用read和write方法来实现对硬件的读写,这种情况下我们把文件结构体看成父类,下面的键盘、屏幕等等看作子类,这不就是多态吗?而这一中调用的方式也叫做虚拟文件操作系统。所以对Linux来说一切皆文件。

4.对文件结构体认识

我们现在要知道当我们在进程中打开一个文件的时候,是怎么打开的:
我们打开一个文件,肯定是要对它进行各种操作,而文件无非就是文件内容+文件属性。所以文件结构体中应该会有文件的属性,以及对文件操作的各种方法集,操作文件不会在磁盘中操作,cpu不跟外设打交道,所以需要将文件加载到内存中,但是文件的加载又不是全部加载进来,而是,在创建好文件结构体的时候内存中还会构建一片文件缓冲区,文件的数据会加载到这里面。而这一系列操作都是由操作系统完成的。
那么我们使用系统调用打开文件后,返回系统标识符,用户根据这个文件标识符,对文件进行操作,而操作系统会到进程pcb中找到标识符指针数组的指针来找到那个数组,然后通过标识符下标来找到那个文件,再执行用户的操作。
在这里插入图片描述
那操作系统是如何对文件实现读写操作的呢?
首先是读:读肯定是读文件缓冲区的内容,当缓冲区中没有内容的的时候就会触发缺页中断,再让磁盘向缓冲区中加载(拷贝)。
写:我们在增加文件数据的时候,无非是开始增加,或者追加增加,这些都不需要加载文件到缓冲区,但是删除文件数据呢?这时候就需要将文件加载到缓冲区中了。而对文件内容的增加或删除在文件缓冲区中执行,然后由操作系统定时刷新文件缓冲区内容到磁盘中(也是一个拷贝的过程)。
那至此,我们得到,不论是文件的读还是写,都需要将文件加载到文件缓冲区中,并且用户对文件的读写对操作系统来说,也只是文件缓冲区的来回拷贝而已。

5. fd的再再认识

我们上面知道,进程创建好之后,会默认打开三个流,那假如我们关闭其中一个,再打开一个文件会怎么样呢?
在这里插入图片描述
在这里插入图片描述
关闭2呢?
在这里插入图片描述

在这里插入图片描述
不关闭
在这里插入图片描述
在这里插入图片描述
那我们就得出了fd的分配规则:寻找最小的,没有被使用的数据的位置,分配给指定的打开的文件
再认识两个个函数write/read:
在这里插入图片描述
在这里插入图片描述
返回值先不做介绍,使用起来很简单。
我们先对write和read和标准输入输出流进行简单的使用
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
需要注意的是这里我们没有将斜杠零算上去,那是因为“字符串结束的标志是斜杠零”这个是只在C语言中,对文件不适用。之后再验证。
在这里插入图片描述
我们再对上面进行一些小修改呢?

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
我们发现两个现象,一个是我们关闭1号文件描述之后对应的文件之后,再次打开一个文件,向1号文件写入时,写到了log.txt里。
第二个是当我们向1号文件里写入字符串时,字符串的斜杠零也被计算入内,但是log.txt中出现了乱码。
要是不计算呢?
在这里插入图片描述
在这里插入图片描述
就不会。
我们再看一个东西:
在这里插入图片描述
我们刚才的行为不就跟重定向一样吗?

a. 追加重定向

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

b. 输入重定向

在这里插入图片描述
在这里插入图片描述
重定向的本质其实就是修改文件描述符表的下标内容
我们上面是直接关闭了01两个流,有没有方法是不通过文件描述符分配规则关闭进行修改呢?
这里就有了dup2函数:
在这里插入图片描述
简而言之就是新的newfd就是最后保留下来的文件下标。
这个函数能实现文件描述符表级别的拷贝

c. 问题的引入

经过上面的了解我们再来一段这样的代码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
关于这个问题,我们之后再说明,并且再次详细阐述重定向的问题以及文件机结构体部分具体成员的认识。

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

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

相关文章

轻量封装WebGPU渲染系统示例<49>- 多种灯光多材质(源码)

实现方式: 1. 全局的灯光和阴影。 2. 球体和矩形平面使用了相同的材质对象。 3. 通过材质自动关联和组装对应的渲染材质功能节点。 4. 共享uniform或storage的buffer对象。 5. 共享shader module对象。 当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/b…

优先考虑类型安全的异构容器

在Java中,异构容器是一种可以存储不同类型元素的容器。为了提高类型安全性,可以使用泛型和类型安全的异构容器,而不是传统的非类型安全容器。下面是一个例子,演示如何使用类型安全的异构容器 import java.util.HashMap; import j…

关于MySQL的bigint问题

MySQL的bigint(8)能存多大数值? MySQL的BIGINT(8)可以存储的数值范围是从-9,223,372,036,854,775,808到9,223,372,036,854,775,807。这是因为BIGINT数据类型在MySQL中使用8字节进行存储,每个字节有8位,所以总共可以表示2^64个不同的整数。 …

INFINI Labs 产品更新 | Easysearch 新增快照搜索功能,Console 支持 OpenSearch 存储

INFINI Labs 产品又更新啦~,包括 Easysearch v1.7.0、Console v1.13.0。本次各产品更新了 Easysearch 快照搜索功能;Console 支持 OpenSearch 集群存储系统数据、优化了初始化安装向导流程等。 以下是本次更新的详细说明。 INFINI Easysearch v1.7.0 …

Linux---查看文件内容命令

1. 查看文件内容命令的使用 命令说明cat查看小型文件more分屏查看大型文件 cat命令的效果图 说明: cat命令结合重定向可以完成多个文件的合并gedit 文件编辑命令,可以查看和编辑文件 more命令的效果图 当查看内容信息过长无法在一屏上显示时,可以使…

Unity中实现ShaderToy卡通火(移植篇)

文章目录 前言一、准备好我们的后处理基础脚本1、C#:2、Shader: 二、开始逐语句对ShaderToy进行转化1、首先,找到我们的主函数 mainImage2、其余的方法全部都是在 mainImage 函数中调用的方法3、替换后的代码(已经没报错了,但是效…

Centos7 配置Git

随笔记录 目录 1, 新建用户 2. 给用户设置密码相关操作 3. 为新用户添加sudo 权限 4. 配置Git 4.1 配置Git 4.2 查看id_ras.pub 5, 登录Git 配置SSH 秘钥 6. Centos7 登录Git 7. clone 指定branch到本地 8. 将新代码复制到指定路径 9. 上传指定代码 …

基于ssm电影网站源码和论文

随着Internet的发展,人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化,网络化和电子化。它将是直接管理电影网站的最新形式。本论文是以构建电影网站为目标,使用 java技术制作,由管理员和用户两大部分组成。…

GEM5 McPAT NoC教程: xml设置汇总-2023版

简介 McPAT的xml有一些参数需要设置,noc的部分很多Gem5ToMcpatparser没有设置,也没有给出如何设置的条件。尤其是和活动相关的total access,不知道具体怎么设置,也不知道如何从gem5 stats.txt中导出。本文提供了2023年的收集到ge…

【强化学习-读书笔记】表格型问题的 Model-Free 方法

参考 Reinforcement Learning, Second Edition An Introduction By Richard S. Sutton and Andrew G. Barto无模型方法 在前面的文章中,我们介绍的是有模型方法(Model-Based)。在强化学习中,"Model"可以理解为算法…

【CSS】前端点点点加载小点样式css动画过程实现

对话的 ... 加载动画&#xff0c;直接用 CSS 就可以实现&#xff0c;样式可以自己改&#xff0c;逻辑大差不差 <div class"loading-text"><span class"dot1"></span><span class"dot2"></span><span class&quo…

动态面板简介以及ERP原型图案列

动态面板简介以及ERP原型图案列 1.Axure动态面板简介2.使用Axure制作ERP登录界面3.使用Asure完成左侧菜单栏4.使用Axuer完成公告栏5.使用Axuer完成左边侧边栏 1.Axure动态面板简介 在Axure RP中&#xff0c;动态面板是一种强大的交互设计工具&#xff0c;它允许你创建可交互的…

pgAdmin 4的安装与使用

一、pgAdmin 4安装 ①访问pgAdmin官网&#xff0c;下载路径如下&#xff1a; pgAdmin下载 ②选择个人电脑所对应的系统&#xff0c;我的电脑是Windows10系统&#xff0c;所以选择Windows ③选择所需要的pgAdmin版本&#xff0c;我选择的是最新的v7.3版本 ④下载完成后双击pg…

Bootstrap在弹框Povoper中显示图片

项目开发需要实现这个效果&#xff0c;当鼠标划过这个按钮的时候&#xff0c;会显示出指定的图片出来 HTML代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"…

《Linux C编程实战》笔记:文件读写

Linux c下文件读写可用creat&#xff0c;open&#xff0c;close&#xff0c;read&#xff0c;write&#xff0c;lseek等函数。对于跨平台的程序还是用C标准库的fopen等。 open #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open …

【️什么是分布式系统的一致性 ?】

&#x1f60a;引言 &#x1f396;️本篇博文约8000字&#xff0c;阅读大约30分钟&#xff0c;亲爱的读者&#xff0c;如果本博文对您有帮助&#xff0c;欢迎点赞关注&#xff01;&#x1f60a;&#x1f60a;&#x1f60a; &#x1f5a5;️什么是分布式系统的一致性 &#xff1f…

基于QTreeWidget实现带Checkbox的多级组织结构选择树

基于QTreeWidget实现带Checkbox的多级组织结构选择树 采用基于QWidgetMingw实现的原生的组织结构树 通过QTreeWidget控件实现的带Checkbox多级组织结构树。 Qt相关系列文章&#xff1a; 一、Qt实现的聊天画面消息气泡 二、基于QTreeWidget实现多级组织结构 三、基于QTreeWidget…

Git忽略已经提交的文件

原理类似于 Android修改submodule的lib包名

FastAdmin后台安装出现2054错误的解决办法

用Navicat修改密码验证方式。MySQL Workbench的Server菜单中的Users and Privileges菜单中似乎不支持此项修改。 修改完毕以后也许会报错&#xff1a; Access denied for user ‘root‘‘localhost‘ (using password: YES) 用以下命令无密进入mysql。 C:\Program Files\MySQ…

基于SSM的校友录设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…