Linux文件(命令行文件操作、Linux文件操作、默认标准文件的底层原理)
1. 命令行的文件操作
在c语言中,我们用 fwrite
和 fread
来读写文件,在写文件时,可以使用 w
来覆盖重写文件内容,也可以用 a
来追加文件内容。实际上读写文件是进程来执行的,文件的打开和关闭是CPU在执行我们的代码。所以在Linux中,也有一套读写文件的操作。
在命令行中 >
命令与w
含义相同,可以覆盖文件内容;而 >>
命令含义与 a
相同,可以在文件后面追加内容。
它们的使用方法如下:
2. Linux的文件操作
Linux中,使用 open()
函数来打开文件,这个函数有两种重载。其中两个参数的重载一般用来打开已经存在的文件 ;而三个函数的重载一般用来创建新的文件。
头文件:
c++
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags)
open()
函数的 flags
参数是一个位图,以整型的形式传递。使用位运算符|
可以以多种方式打开文件:
O_RDONLY
: 以只读方式打开文件。
O_WRONLY
: 以只写方式打开文件。
O_RDWR
: 以读写方式打开文件。
O_CREAT
: 如果文件不存在,则创建文件。
O_EXCL
: 与O_CREAT
一起使用,如果文件存在,则打开失败。
O_TRUNC
: 如果文件存在且以写方式打开,则将其长度截断为0。
O_APPEND
: 在每次写入时都追加到文件末尾。
O_NONBLOCK
: 以非阻塞模式打开文件。
int open(const char *pathname, int flags, mode_t mode)
mode
参数为该文件的权限,以权限掩码的形式传参 。但要注意,系统已经存在默认的权限掩码,直接设置参数会影响文件最终的权限掩码。所以在设置文件的权限掩码时,还需要使用 umask()
函数来重置默认权限掩码,该函数不会影响系统的权限掩码,代码中的权限掩码因为就近原则使用重置的默认权限掩码。
mode_t umask(mode_t mask)
3. 理解文件操作
3.1 读写文件的底层原理
文件存储在硬盘中,所以向文件写入本质是向硬盘写入 。OS是文件的管理者,用户没有权限直接向文件写入,必须通过OS写入。所以文件写入必须使用系统调用,而我们使用的c和c++都是系统调用的封装。
因为文件储存在硬盘中,而OS在内存里,所以OS并不直接管理文件,而是使用类似于PCB的结构体来管理文件。
在Linux中,进程的task_struck里有一个指向files_struct结构体的指针,在这个结构体中存储了一个fd_array数组,这个数组记录着进程打开的文件的个数。
一个进程可以打开多个文件,文件描述符fd的本质是内核的进程,是文件映射关系的数组下标。其中,用户打开的文件下标从3开始,因为0、1、2分别存储着:
0 | 标准输入 | 键盘 |
---|---|---|
1 | 标准输出 | 显示器 |
2 | 标准错误 | 显示器 |
在c语言的文件操作中也是如此,只是名字不同,分别对应为:
stdin | 标准输入 | 键盘 |
---|---|---|
stdout | 标准输出 | 显示器 |
stderr | 标准错误 | 显示器 |
被打开的文件用双向链表相互连接,而OS通过id记录的下标可以直接找到对应的文件。
文件描述符fd的分配规则:
查找自己的文件描述表,分配最小的没有被使用的fd。(实际上也可以把0、1、2文件关闭,这样新分配的fd也会去用这三个数字)。
对文件的读和写操作都不是直接对磁盘中的文件读写,而是有一个文件内核的缓冲区 。无论读写,都必须在合适的时候让OS把文件的内容读到文件缓冲区中。
所以我们在读写文件时,过程是:
读:OS通过fd找到对应的struct_file > 将硬盘中文件内容加载入文件内核的缓存 >将缓存内容拷贝到用户指定的区域
写:OS通过fd找到对应的struct_file >将写的内容拷贝入文件内核的缓存 > 将缓存的内容写入磁盘中的文件里
OS通过fd来查找文件,实际上OS只认fd,那么c和c++在使用读写文件的操作时,为什么没有使用fd也能操作文件呢?
在c语言中, FILE
实际上是库中提供的一种结构体,在这个结构体中实际也存储了文件的 fd ,因为 Linux 操作系统只认 fd ,C语言通过这个结构体让用户规避了直接接触 fd 描述符。在 c++ 中的 cin
和 cout
也是如此,本质上都会调用 fd 描述符。
C 和 C++ 设计自己进行文件操作接口的原因,是因为语言都要有跨平台性 。系统不同,系统调用的接口可能不一样,如 Linux 中使用文件描述符 fd 对文件进行管理和操作,但 Windows 中没有文件描述符的概念,所以直接使用系统调用的代码不具有跨平台性。
但如果语言对不同平台的系统调用进行封装,将不同操作系统的系统调用封装成一样的接口。虽然底层使用的系统调用不同,但是顶层的语言接口使用方式相同,这样语言也就有了跨平台性。
3.2 默认标准文件的底层原理
Linux 中一切皆文件,所以fd的0
、1
、2
下标中的键盘和显示器虽然是硬件,但在 Linux 中也是文件。同样的OS不直接管理硬件,而是通过管理结构体来间接管理硬件。
在 struct_file
结构体中有一个 device
结构体记录着文件的属性,还有两个函数指针分别指向驱动中实现读和写的函数,OS就是通过这种方式来以文件的形式来管理硬件。这种管理方式被称为 vfs (virtual file system)