全文目录
- C语言的文件操作函数
- 系统调用接口
- open
- write
- read
- close
- Linux中一切皆文件
- 文件描述符
- 重定向
- 缓冲区
- 为什么`fflush`能直接找到缓冲区进行刷新
- 文件系统
C语言的文件操作函数
参考文章:
- C语言文件操作【基础知识 + 顺序读写 + 文件版通讯录】
- C语言文件操作收尾【随机读写 + 结束判定 + 文件缓冲区】
注意事项:
- 通过接口创建的文件是在当前路径下(进程的工作路径)
- C语言中的字符串结尾
\0
在文件中不起效,会出现乱码,所以向文件写入时不要将\0
写入。
系统调用接口
open
mode:创建文件时文件的权限,同样受到umask掩码的限制。
在进程中可以通过umask函数来设置该进程上下文的掩码
write
read
close
Linux中一切皆文件
OS中需要管理大量的硬件,不同硬件的读写方法都是不同的,意味着存在大量的读写函数。同时为了给用户提供良好的服务,要让用户看到所有的软硬件都是使用同样的接口 (write和read
)。那么在创建文件描述符时的会根据不同的文件描述符和文件类型匹配不同的读写函数。
通过回调函数的方式调用具体的读写函数,在上层看来调用的都是文件的读写函数,所以一切皆文件。同样的,这也是多态的一种表现形式。
我们将存在于OS中的,文件属性数据结构的集合,称为虚拟文件系统(vfs)。通过对虚拟文件系统的管理,就可以摒弃掉底层硬件之间的差别,而统一使用文件接口的方式,来进行所有的文件(硬件)操作。
文件描述符
内核 ( k e r n e l ) (kernel) (kernel)利用文件描述符 ( f i l e d e s c r i p t o r ) (file descriptor) (filedescriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。 —— 百度百科
OS中的文件管理:
一个进程会打开大量的文件,被打开的文件需要管理,就会有文件相关的数据结构
struct file
来描述文件(内容 + 属性),不同的文件通过双链表链接起来。并通过指针数组file* fd_array[]
存放每个结构体的指针。 进程的PCB结构体中同样通过结构体struct files_struct
来描述这个数组指针。文件描述符的本质就是存放struct file
的指针数组的下标。
C语言的库函数原理:
在C语言程序中默认会打开三个标准输入输出流: s t d i n 、 s t d o u t 、 s t d e r r stdin、stdout、stderr stdin、stdout、stderr,在OS中默认占用0、1、2三个文件描述符。C语言中的
FILE
结构体中包含系统的文件描述符,并在底层通过系统调用接口进行文件读写。
分配规则:
分配最小的、没有被占用的文件描述符
重定向
在Linux中一切皆文件,标准输入输出流也是文件。利用文件描述符的分配规则就能将标准输入输出流的文件描述符指向指定的文件,实现重定向。
本质:更改文件描述符的指向
系统调用接口
标准错误重定向:
./myfile > 1.txt 2>2.txt
# 将1.txt的文件描述符指向的内容拷贝到1号文件描述符中
# 将2.txt的文件描述符指向的内容拷贝到2号文件描述符中
./myfile > 1.txt 2>&1
# 将1.txt的文件描述符指向的内容拷贝到1号文件描述符中
# 同时将1号文件描述符指向的内容拷贝到2号文件描述符中
缓冲区
为了提高整机效率,减少IO次数,C语言给每个文件提供了缓冲区,所有的硬件都是倾向于全缓冲的。但为了提高用户的体验,对于不同的设备,会采取不同的刷新策略。
刷新策略:
- 显示器【行缓冲】
- 磁盘【全缓冲】
缓冲区内的数据也是进程的数据,所以当fork创建子进程时也会发生写时拷贝。对于不同的设备,同样的代码可能发生不一样的结果。
为什么fflush
能直接找到缓冲区进行刷新
在C语言层面,描述文件的是FILE
类型的结构体,里面不仅仅封装了文件描述符,还包含了缓冲区信息。也就
syncfs(fd); # 将缓冲区的数据刷新到磁盘
文件系统
Linux管理磁盘上的文件时,是将内容和属性分开存储的
文件系统结构
总结:
-
其中的super block 和 GDT存放整个文件系统的对应信息。
-
目录也是文件,目录的datas block 中存放着文件名和inode编号的映射关系(目录权限对应目录的操作)
-
软链接就是创建快捷方式,有独立的inode编号
-
硬链接就是起别名,通过引用计数的方式共用同一数据
-
目录的起始链接数就是2, 当目录中多了一个目录计数++