目录
预备知识
我们知道,创建出一个空文件也要在内存中占空间。
文件=文件内容+文件属性
操作文件本质就是操作文件内容或文件属性。
我们在访问文件之前,都需要用编辑软件先打开这个文件,操作文件本质都是通过执行代码的方式完成操作的,都是通过被CPU处理来完成的。所以,文件在操作之前必须创建进程、将文件加载到内存中,让CPU去调度这个进程来完成文件操作。文件本质是被进程打开的。
进程可以打开多个文件。
在某一时间段内,系统中存在多个进程,也可能同时存在更多的被进程打开的文件,操作系统要管理这些被多个进程打开的文件,就需要"先描述,再组织"。
内核中一定要有描述被打开的文件的结构体,并定义其对象管理每个被进程打开的文件,并将其节点以某种数据结构连接。此后,对打开文件的管理就变成了对某种数据结构的增删查改了。
操作这个文件是通过操作系统执行对应的系统调用接口来实现的。
系统没有将磁盘中的所有文件都打开,被进程打开的文件在内存中称为内存文件,没有被进程打开的文件在磁盘中称为磁盘文件。
文件操作接口
打开文件接口
重定向与文件操作关系
"w"方式与重定向
"a"方式与追加重定向
总结一下
重定向操作的前提是先打开此文件,本质是向文件内做写入操作。
重定向>时,本质是先以"w"方式打开(会提前自动清空),然后再写入。
追加重定向>>时,本质是将文件以"a"方式打开,从文件结尾处开始追加写入。
写入文件接口
进程在启动时,会自动记录自己启动时所在的目录路径,默认将当前目录路径存储在/proc/进程pid这个文件夹中的cwd连接文件中。
如果需要修改,则需要使用chdir接口。
cwd文件记录当前目录路径,我们在使用fopen接口的时候,默认就是在当前目录下打开文件,就是借助了cwd文件,由cwd文件和fopen接口的第一个参数进行拼接为绝对路径来寻找文件。
读取文件接口
在执行文件写入操作过程中,首先创建指定的空文件(文件存在就不创建),由操作系统写入到内存(缓冲区)中,执行到文件退出时,再由操作系统将缓冲区的内容刷新到磁盘文件中。
程序在启动时,会默认打开三个文件流,stdin、stdout、stderr。
extern FILE *stdin; //标准输入流
extern FILE *stdout; //标准输出流
extern FILE *stderr; //标准错误流
操作系统设计者在设计的时候,考虑到用户从何处输入数据、将输出信息输出到何处、遇到错误之后将错误记录到何处。基于这样的考虑和人们使用这三个文件频率足够高,所以设计成程序启动时默认打开,程序退出时默认关闭。
如果程序默认不帮我们打开这三个文件流,那么我们在做指定操作的时候,就需要打开指定的文件流。比如:我们在输入的时候,就要提前打开stdin文件流,才能进行输入操作。
程序在启动时,这三个文件被默认打开,可以直接使用,我们使用对应的接口时(sacnf/printf)内部一定是对这三个文件进行操作。一般来说可以认为显示器文件就是stdout、键盘文件就是stdin。键盘、显示器都是硬件,我们对其进行读写操作时,底层必须使用操作系统提供的系统调用接口,完成从上层到底层硬件的操作过程。
系统调用接口
访问文件不仅仅有语言上的访问文件接口,操作系统必须要提供对应的访问文件的系统调用接口。
参数解析
const char *pathname:文件所在路径
int flags:文件打开方式
O_RDONLY:只读模式
O_WRONLY:写方式
O_RDWR :读写方式
O_CREAT :若文件不存在就创建
O_TRUNC :写入之前清空文件
O_APPEND:从文件结尾处开始追加写入
采用open系统调用,以O_WRONLY方式写入时,如果当前目录下没有该文件,就会报错。
如果不存在就创建则需要带上O_CREAT参数,但创建出来的文件的权限为会出现乱码现象,所以就需要第三个参数mode_t mode(文件起始权限)。
mode_t mode:文件起始权限
mode参数是起始权限会被umask过滤为最终权限,mode格式为0xxx(xxx为八进制权限数字)。
但是,我们如果不想使用系统的umask,想为此进程自定义一个umask,就需要使用umask接口。
在系统层面,每一个文件都有一个特有的文件描述符,通过这个文件描述符来控制文件,对此文件操作都是借助文件描述符。
需要在写入之前做一下清空,就需要带上O_TRUNC选项。