【Linux/OS学习】基础文件控制/IO——内存文件

文章目录

    • 一、 基础文件控制
      • 1.1 系统接口open函数
      • 1.2 Linux中文件描述符
      • 1.2 C语言FILE中的文件描述符
    • 二、重定向
      • 1. 输出重定向
      • 2. 追加重定向
      • 3. 输入重定向
    • tips:fd的分配规则

一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀在这里插入图片描述

一、 基础文件控制

  • 以下为C语言中默认打开的三个文件流(C++中为cin、cout、cerr)
    在这里插入图片描述
    值得一提的是:以上三个默认打开的文件分别为键盘文件、显示器文件、显示器文件,在任一进程中使用系统接口(如open/read/write时)时依次对应(文件描述符fd)下标0、1、2

头文件<unistd.h>定义了常量 STDIN_FILENO、STDOUT_FILENO和 STDERR_FILENO,它们可用来代替显式的描述符值。

  • 对应功能:0标准输入, 1标准输出, 2标准错误

标准输出流和标准错误流的区别利用管道可以更好解释,不在此文解释


1.1 系统接口open函数

在这里插入图片描述

函数返回值是新打开文件的文件描述符

函数第一个参数pathname,表示要打开或创建的目标文件。

  • 若pathname以绝对路径的方式给出,则当需要创建该文件时,就在pathname路径下进行创建。
  • 若pathname只以文件名的方式给出,则当需要创建该文件时,默认在当前工作路径下进行创建。

函数的第二个参数是flags,表示打开文件的方式。

其中常用选项有如下几个:

参数选项含义
O_RDONLY以只读的方式打开文件
O_WRNOLY以只写的方式打开文件
O_APPEND以追加的方式打开文件
O_RDWR以读写的方式打开文件
O_CREAT当目标文件不存在时,创建文件
O_TRUNC打开文件的时候会将文件原本的内容全部丢弃,文件大小变为 0

打开文件时,可以传入多个参数选项,用“或”运算符隔开。
若想以只写的方式打开文件,但当目标文件不存在时自动创建文件,则第二个参数设置为O_WRONLY | O_CREAT
值得一提的是,flags的类型为int,而这些参数的本质也确实是。

宏定义选项的共同点就是,它们的二进制序列当中有且只有一个比特位是1(O_RDONLY选项的二进制序列为全0,表示O_RDONLY选项为默认选项),且为1的比特位是各不相同的,这样一来,在open函数内部就可以通过使用**“与”运算**来判断是否设置了某一选项。

在这里插入图片描述

函数的第三个参数是mode,表示创建文件的默认权限。

将mode设置为0666,则理论上文件创建出来的权限如下:
在这里插入图片描述

但实际上创建出来文件的权限值还会受到umask(文件默认掩码)的影响,实际创建出来文件的权限为:mode&(~umask)umask的默认值一般为0002,当我们设置mode值为0666时实际创建出来文件的权限为0664。
在这里插入图片描述

若想创建出来文件的权限值不受umask的影响,则需要在创建文件前使用umask函数将文件默认掩码设置为0。

umask(0); //将文件默认掩码设置为0

注意: 当不需要创建文件时,open的第三个参数可以不必设置

1.2 Linux中文件描述符

文件描述符fd(file descriptor)就是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符来实现。

Linux内核中task_struct 结构体作为进程的抽象封装,是 Linux 里面最复杂的结构体之一 ,成员字段非常多,不仅包括描述虚拟空间的mm_struct,还有描述管理文件的file_struct结构体,这个结构体为管理某进程打开的所有文件的管理结构

下列为file_struct代码与部分task_struct代码

 struct task_struct {//此处只列出与本文相关部分// ...struct mm_struct		*mm;//struct mm_struct		*active_mm;/* Open file information: */struct files_struct     *files;// ...
}struct files_struct {// 读相关字段atomic_t count;bool resize_in_progress;wait_queue_head_t resize_wait;// 打开的文件管理结构struct fdtable __rcu *fdt;struct fdtable fdtab;// 写相关字段unsigned int next_fd;unsigned long close_on_exec_init[1];unsigned long open_fds_init[1];unsigned long full_fds_bits_init[1];struct file * fd_array[NR_OPEN_DEFAULT];
};

files_struct 本质上就是用数组管理的方式来管理所有打开的文件。该进程下所有打开的文件结构都在数组里。数组在那里?有两个地方:

  • struct file * fd_array[NR_OPEN_DEFAULT] 是一个静态数组,随着 files_struct 结构体分配出来的,在 64 位系统上,静态数组大小为 64;
  • struct fdtable 可以理解成用于存储文件结构体的一个动态数组,数组边界是用字段描述的;
    简化结构体如下:
struct fdtable {unsigned int max_fds;struct file __rcu **fd;      /* current fd array */
};

其中 max_fds 指明数组边界
fdtable.fd 指向的内存地址还是存储指针的(指针类型为 struct file * )。即 fdtable.fd (二级指针)指向一个数组,数组元素为指针(指针类型为 struct file *)

以上这种的动静态结合设计使得可以方便地动态分配和替换文件描述符数组,以满足需要扩展文件描述符数组的情况。

下图为了表示简便,未展示fdtable
在这里插入图片描述

综上,不难理解fd 即管理struct file*数组的索引,也就是数组的槽位编号而已。 通过非负数 fd 就能拿到对应的 struct file 结构体的地址。


而struct file结构体是用来标识进程打开的某一个文件。简化结构如下:

struct file {// ...struct path                     f_path;struct inode                    *f_inode;const struct file_operations    *f_op;atomic_long_t                    f_count;unsigned int                     f_flags;fmode_t                          f_mode;struct mutex                     f_pos_lock;loff_t                           f_pos;struct fown_struct               f_owner;// ...
}

下面为 IO 相关的几个重要的字段:

  • f_path :标识文件名
  • f_inode :inode 这个是 vfs 的 inode 类型,是基于具体文件系统之上的抽象封装;
  • f_pos : 当前文件偏移。f_pos 在 open 的时候会设置成默认值,seek 的时候可以更改,从而影响到 write/read 的位置;

注意

  • struct file 是属于系统级别的结构,是可以共享与多个不同的进程。即多个进程同时指向一个文件(如fork出的父子进程)。
  • 在同一个进程中,多个 fd 可能指向同一个 file 结构,利用dup函数即可
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);

1.2 C语言FILE中的文件描述符

语言的库函数都是对系统调用接口的封装,本质上访问文件都是通过文件描述符fd进行访问的,所以C库当中的FILE结构体内部必定封装了文件描述符fd。

首先,我们在/usr/include/stdio.h头文件中可以看到下面这句代码,也就是说FILE实际上就是struct _IO_FILE结构体的一个别名。

typedef struct _IO_FILE FILE;

而我们在/usr/include/libio.h头文件中可以找到struct _IO_FILE结构体的定义,在该结构体的众多成员当中,我们可以看到一个名为_fileno的成员,这个成员实际上就是封装的文件描述符。
在这里插入图片描述

所以C语言当中的其他文件操作函数,如fread、fwrite、fputs、fgets等函数都是在做什么?

fopen函数在上层为用户申请FILE结构体变量,并返回该结构体的地址(FILE*),在底层通过系统接口open打开对应的文件,得到文件描述符fd,把fd填充到FILE结构体当中的_fileno变量中,至此便完成了文件的打开操作。

之后这些操作函数都是根据我们传入的文件指针找到对应的FILE结构体,然后在FILE结构体当中找到文件描述符,最后通过文件描述符调用系统接口对文件进行的一系列操作


二、重定向

重定向格式 :流 >/>> 文件
重定向有三种类型:

  • 输出重定向:将本应该打印到 显示器 的内容覆盖式输出到了指定的文件中。
  • 追加重定向:将本应该打印到 显示器 的内容追加式的输出到了指定的文件中
  • 输入重定向:将本应该从 键盘中 读取的内容改为从指定的文件中读取。

重定向的本质是:修改文件描述符fd下标 对应的struct file * 的内容 (将其换成目标文件)。

1. 输出重定向

使用重定向操作符“>”,后面接文件名,就可以把标准输出重定向到另一个文件中,而不是显示在屏幕上。其实现的本质可以利用以下代码理解

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{close(1);int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);if (fd < 0){perror("open");return 1;}printf("hello world\n");fflush(stdout);close(fd);return 0;
}

fd为1的标准输出文件被关闭,紧接着打开的log.txt文件fd即为1,运行后显示器上并没有输出数据,对应数据输出到了log.txt文件当中。
printf函数是默认向stdout输出数据的,而stdout指向的FILE结构体中存储的文件描述符就是1,因此printf实际上就是向文件描述符为1的文件输出数据
在这里插入图片描述
这与cat >log1.test的作用其实一样,因为cat xx.txt > other.txt 其实这种写法其实是简写,把1省略了,完整写法cat xx.txt 1> other.txt。,即将显示器 的内容覆盖式输出到了指定的文件中

在这里插入图片描述

2. 追加重定向

重定向符号>>

追加重定向与输出重定向的区别在于前者为追加式输出
原理测试方法与上式相同,但在open函数中需要将第二个参数添加O_APPEND以实现追加输入

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

3. 输入重定向

当命令执行后我们并不希望得到输出,而是想把这个输出丢弃,尤其是在输出错误和状态信息的情况下更为需要。系统提供了一种方法,即通过把输出重定向到一个称为/dev/null的特殊文件中来实现它。这个文件是一个称为位桶(bit bucket)的系统设备,它接受输入但是不对输入进行任何处理
>/dev/null : 这是一种简写,完整的写法是 1 >/dev/null 。 在linux中,默认的重定方向就是保证输出流,也就是1 。那么2>&1 又是什么意思呢? 也很简单,这也是重定向的结构。2表示标准错误流。& 表示等同的意思,也就是说跟1的情况一样,跟1采取相同方式,即标准错误流跟标准输出流采取同样会的处理方式,也就是重定向到空设备。这个后缀经常使用在linux命令中,表示不输出任何内容。


tips:fd的分配规则

当有新的文件被打开,需要分配fd时,在fd array[]中从小到大,按照顺序找最小的且没有被占用的fd,来进行分配;

具体验证可以采用系统接口close(1)关闭原来的显示器文件,新建文件后在向该文件cat 输入内容,会发现该内容也会在显示器上打印,即该文件此时的fd为1。
(注意scanf函数是默认从stdin读取数据的,而stdin指向的FILE结构体中存储的文件描述符是0,因此scanf实际上就是向文件描述符为0的文件读取数据。

  1 #include <stdio.h>2 #include <sys/types.h>3 #include <sys/stat.h>4 #include <fcntl.h>5 #include <unistd.h>6 7 int main()8 {9    10      close(1);1112     umask(0000);13     int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);//没有指明文件路径,默认在当前进程的工作目录14     if(fd<0)15     {16         perror("open");17         return 1;18     }19 20     printf("open fd:%d\n",fd);// printf --> stdout21     fprintf(stdout,"open fd:%d\n",fd);// fprintf --> stdout 22 23     fflush(stdout);                                                                                                                                  24     close(fd);25     return 0;26 }

实际上我们使用重定向时,重定向的是文件描述符是1的标准输出流,而并不会对文件描述符是2的标准错误流进行重定向。


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

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

相关文章

进电子厂了,感触颇多...

作者&#xff1a;三哥 个人网站&#xff1a;https://j3code.cn 本文已收录到语雀&#xff1a;https://www.yuque.com/j3code/me-public-note/lpgzm6y2nv9iw8ec 是的&#xff0c;真进电子厂了&#xff0c;但主人公不是我。 虽然我不是主人公&#xff0c;但是我经历的过程是和主…

画图--添加了两条水平直线,分别在y=0.092和y=-0.092处。并在水平直线上添加文本

import matplotlib.pyplot as plt import pandas as pd plt.rcParams[font.sans-serif]=[SimHei] #用来正常显示中文标签 plt.rcParams[axes.unicode_minus]=False #用来正常显示负号import matplotlib.pyplot as plt import pandas as pd# 示例函数,绘制曲率变化率图 def dra…

jdk1.8下载与安装 图文版

JDK下载 首先在Oracle官网上下载jdk1.8.https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html,如下图所示&#xff0c;找到jdk1.8,选择对应的版本 或者 链接&#xff1a;https://pan.baidu.com/s/13lZy7JKE1xn-dXx_VG1QFA?pwd29wl 提取码&#xff1a;…

谷歌承认“窃取”OpenAI模型关键信息

什么&#xff1f;谷歌成功偷家OpenAI&#xff0c;还窃取到了gpt-3.5-turbo关键信息&#xff1f;&#xff1f;&#xff1f; 是的&#xff0c;你没看错。 根据谷歌自己的说法&#xff0c;它不仅还原了OpenAI大模型的整个投影矩阵&#xff08;projection matrix&#xff09;&…

Redis高可用之哨兵模式和集群模式

Redis高可用 Redis哨兵高可用 概述 sentinel哨兵是特殊的redis服务&#xff0c;不提供读写服务&#xff0c;主要用来监控redis实例节点。 哨兵架构下client端第一次从哨兵找出redis的主节点&#xff0c;后续就直接访问redis的主节点 不会每次都通过sentinel代理访问redis的主…

路由算法与路由协议

路由选择协议的核心是路由算法&#xff0c;即需要何种算法来获得路由表中的各个项目。 路由算法的目的很简单&#xff1a;给定一组路由器以及连接路由器的链路&#xff0c;路由算法要找到一条从源路由器到目标路由器的最佳路径。通常&#xff0c;最佳路径是指具有最低费用的路…

知识图谱技术综述

作者简介:徐增林(1980 − ),男,博士,教授,主要从事机器学习及其在社会网络分析、互联网、计算生物学、信息安全等方面的研究. 【摘要】 知识图谱技术是人工智能技术的重要组成部分,其建立的具有语义处理能力与开放互联能力的知识库,可在智能搜索、智能问答、个性化推…

TodoList案例——静态组件

Todo-List案例 组件化编码流程 1、实现静态组件&#xff1a;抽取组件&#xff0c;使用组件实现静态页面效果 2、展示动态数据&#xff1a; 2.1数据的类型、名称是什么&#xff1f; 2.2数据保存在哪个组件&#xff1f; 3、交互——从绑定事件监听开始 演示&#xff1a; App组…

九、软考-系统架构设计师笔记-软件可靠性基础知识

1、软件可靠性概念 软件可靠性定义 软件可靠性(Software Reliability)是软件产品在规定的条件下和规定的时间区间完成规定功能的能力。 规定的条件是指直接与软件运行相关的使用该软件的计算机系统的状态和软件的输入条件&#xff0c;或统称为软件运行时的外部输入条件。规定…

自反检索增强生成 (SELF-RAG)

理想情况下&#xff0c;生成式 AI 的落地应用需要提供真实且高质量的响应。SELF-RAG 正是为解决此问题而被创建的。 背景介绍 有趣的是&#xff0c;RAG的发展轨迹与提示工程&#xff08;prompt engineering&#xff09;非常相似。RAG 最初是一个简单而有效的概念&#xff0c;其…

Rust 的 Error 如何使用?

在 Rust 中&#xff0c;错误处理是一个重要且核心的概念。Rust 提供了一种强大的类型系统&#xff0c;允许你明确地处理可能出现的错误。std::error::Error trait 是 Rust 标准库中用于表示错误的基础 trait。实现这个 trait 的类型可以被用作错误类型&#xff0c;并且可以使用…

cocos2d-x-3.17 android升级 gradle NDK_DEBUG=0 -o NDK_DEBUG=1 -o cocos2dlua_shared

由于需要升级sdk版本 需要对应升级gradle版本 记录下升级内容 externalNativeBuild { ndkBuild { - //arguments NDK_DEBUG0 -o 修改成下面 arguments NDK_DEBUG0 } } debug { …

音频功率放大器方案LM4863替代DP4863

音频放大器是在产生声音的输出元件上重建输入的音频信号的设备&#xff0c;其重建的信号音量和功率级都要理想——如实、有效且失真低。音频范围为约20Hz&#xff5e;20000Hz&#xff0c;因此放大器在此范围内必须有良好的频率响应。根据应用的不同&#xff0c;功率大小差异很大…

spring security oauth2 的 scope 概念

在Spring Security OAuth2中&#xff0c;scope用于定义客户端应用程序可以访问的资源范围。这些范围决定了客户端应用程序可以向授权服务器请求哪些权限和资源。以下是一些常见的OAuth2 scope&#xff1a; read: 这个scope允许客户端应用程序读取用户的资源。例如&#xff0c;如…

光学硬件——二向色片

二向色镜&#xff08;Dichroic Mirrors &#xff09;又称双色镜&#xff0c;常用于激光技术中。 产品介绍&#xff1a; 指45度入射或大角度入射时&#xff0c;把光源分离出特定的光谱改变部分光谱光路方向&#xff0c;常用于酶标仪器、荧光显微镜系统、投影光引擎系统、激光灯…

一站式数据采集物联网平台:智能化解决方案,让数据管理更高效、更安全

JVS物联网平台的定位 JVS是企业信息化的“一站式解决方案”&#xff0c;其中包括了基础的数字化底座、各种企业级能力、企业内常见的应用&#xff0c;如下图所示&#xff1a; 整体平台能力层有三大基础能力&#xff1a; 低代码用于业务的定义;数据分析套件用于数据的自助式分…

2024年1月饮料行业数据:热销品牌商品排行榜出炉!

鲸参谋监测的某东平台1月份饮料市场销售数据已出炉&#xff01; 根据鲸参谋电商数据分析平台显示&#xff0c;今年1月份&#xff0c;某东平台上饮料的销量约770万件&#xff0c;环比上个月增长62%&#xff0c;同比去年下滑35%&#xff1b;销售额约25亿元&#xff0c;环比上个月…

Clickhouse数据库应用

目录 ClickHouse的特点和应用优势 应用场景 操作数据库 建表 同步数据 数据查询分析 注意事项 Clickhouse 官网地址&#xff1a; https://clickhouse.tech ClickHouse是一个由俄罗斯的Yandex于2016年开源的面向OLAP&#xff08;联机分析处理&#xff09;的列式数据库管理…

【数据结构】特殊的线性表——栈

&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;个人主页&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388; &#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;数据结构专栏&#x1f388;&#x1f388;&#x1f388;&…

【毕业论文】基于Python的论文管理系统设计与实现+核心内容

&#x1f50d; “学术助手”&#xff1a;基于Python的论文管理系统设计与实现 在学术研究的世界里&#xff0c;论文管理是一项既繁琐又重要的任务。从文献收集、资料整理到论文撰写和版本控制&#xff0c;每一个环节都对研究工作的效率和质量有着直接影响。本文将介绍如何利用…