【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.hqwc.cn/news/535712.html

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

相关文章

基于springboot+vue的电子商务系统(源码+论文)

目录 前言 一、功能设计 二、功能实现 三、库表设计 四、论文 前言 各种购物网站现在已经成了生活中不可缺少的调味品,比如比较全面的淘宝网,还有可以进行交流问答的小红书APP,还有电脑爱好者者们的天堂京东商城等等。拥有一个功能丰富、操作方便的电子商务销售网站,可以汇…

十二:多线程服务端实现

1 理解线程 1.1 引入线程背景 多进程模型有如下缺陷&#xff1a; 创建进程的过程会带来一定的开销。为了完成进程间的数据交换&#xff0c;需要特殊的IPC技术每秒少则数十次&#xff0c;多则数千次的‘上下文切换’是创建进程时最大的开销 运行程序前需要将相应进程信息读入…

Python实战:采集全国5A景点名单

本文将以采集全国 5A 景点名单为例&#xff0c;详细介绍如何使用 Python 进行数据采集。 本文采集到全国340家5A景区的名单&#xff0c;包括景区名称、地区、 A级、评定年份这些字段。 一、分析数据源 为了获取权威数据&#xff0c;我们来到主管部门的官方网站&#xff0c;在右…

学生打架校园防霸凌系统可以监测到吗

随着社会的进步和教育的发展&#xff0c;校园安全问题日益受到社会各界的关注。其中&#xff0c;学生打架和校园霸凌问题尤为突出&#xff0c;不仅影响了学生的身心健康&#xff0c;也破坏了校园的和谐氛围。为了有效预防和应对这些问题&#xff0c;许多学校开始引入校园防霸凌…

origin修改图例为显示”长名称/单位/注释/自定义“等

背景 由于在origin作图时希望修改自动显示的图例&#xff0c;但每次手动更新又比较繁琐&#xff08;特别是在数据量较多的情况下&#xff09;&#xff0c;为了一劳永逸 步骤 1. 在数据工作表中设置好需要修改后的名称&#xff08;我写到长名称里了&#xff09; 2. 修改图例的…

【原创】[新增]ARCGIS之土地报备Txt、征地Xls格式批量导出Por旗舰版

一、软件简介 2024年新增旗舰版软件&#xff0c;本软件全新界面开发&#xff0c;保留原有软件功能及一些使用习惯&#xff0c;并集成了现已有的所有定制格式的支持&#xff0c;并增加自定义格式的导出&#xff1b;做到1N2&#xff08;即为1种通用版本N种定制格式导出txt、Xls&a…

C++ 作业 24/3/13

1、设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数。 #include <iostream>using namespace std;c…

IU5070E线性单节锂电池充电管理IC

IU5070E是一款具有太阳能板最大功率点跟踪MPPT功能&#xff0c;单节锂离子电池线性充电器&#xff0c;最高支持1.5A的充电电流&#xff0c;支持非稳压适配器。同时输入电流限制精度和启动序列使得这款芯片能够符合USB-IF涌入电流规范。 IU5070E具有动态电源路径管理(DPPM)功能&…

数据库管理-第160期 Oracle Vector DB AI-11(20240312)

数据库管理160期 2024-03-12 数据库管理-第160期 Oracle Vector DB & AI-11&#xff08;20240312&#xff09;1 向量的函数操作to_vector()将vector转换为标准值vector_norm()vector_dimension_count()vector_dimension_format() 2 将向量转换为字符串或CLOBvector_seriali…

用友U8 Cloud base64 SQL注入漏洞复现

0x01 产品简介 用友U8 Cloud是用友推出的新一代云ERP&#xff0c;主要聚焦成长型、创新型企业&#xff0c;提供企业级云ERP整体解决方案。 0x02 漏洞概述 用友U8 Cloud base64接口处存在SQL注入漏洞&#xff0c;未授权的攻击者可通过此漏洞获取数据库权限&#xff0c;从而盗…

Ubuntu 系统的基础操作

一. VMware虚拟机安装Ubuntu20.04 安装好就可以进系统了 二. Xshell连接Ubuntu 1.配置网络 2.去连接Xshell 然后输入用户名 xyl 和密码 123 就可以登录上去 三. Ubuntu的使用 1.简介和下载地址 简介&#xff1a; Ubuntu&#xff08;乌班图&#xff09;是一个基于Debian的以…

企智汇数字化项目管理平台,助力企业高效项目管理!数字化转型必备!

数字化项目管理平台是一种集成了先进项目信息技术的管理工具&#xff0c;旨在帮助组织更有效地管理项目&#xff0c;实现项目目标的顺利完成。以下是企智汇数字化项目管理平台的一些核心特点和功能&#xff1a; 1. 统一的信息管理&#xff1a;企智汇数字化项目管理平台能够将项…