引子:
- 文件 = 内容 + 属性
- 文件分为:打开的文件 (进程打开)、 没打开的文件(存储在磁盘中)
- 打开的文件一定在内存中,因为CPU只能和内存进行互动。如果要管理打开的文件,那么必定存在包含文件属性等信息的文件结构体;
一、C语言文件接口回顾
#include <stdio.h> —— 标准输入/输出头文件
FILE * fopen(const char* path, const char* mode); —— 打开文件,返回文件指针,输入文件路径(如果只有文件名,默认为当前进程的当前路径cwd),打开方式”w“,"r","a"等;
- 如果以"w"方式打开,如果路径下该文件不存在,会自动创建这个文件,并且会清空该文件从头开始写; —— 本质上和输出重定向">"是一样的;
- 改变工作路径"#include<unistd.h> int chdir(const char * path)"
- "a":(writing at end of file)文件不存在也会创建文件;—— 本质上和追加重定向">>"是一样的;
int fclose(FILE * fp); —— 关闭文件,关闭成功返回0;
size_t fwrite(const void* ptr[写入内容的起始地址], size_t size[基本大小,如果是字符串最后的'\0'不要传进去,以‘\0’作为字符串结尾是C语言字符串的特殊规定,文件不是这样规定的,加进去会乱码], size_t nmemb[基本大小的个数], FILE* stream[写入的文件指针]);
二、系统IO理解
文件其实是在磁盘上的,磁盘是外部设备,因此访问任意磁盘文件本质上就是在访问硬件;而用户不可能越过操作系统和驱动直接访问硬件,因此所有的语言的库函数,只要是涉及到访问硬件的,函数内部一定都封装了系统调用接口;
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char* pathname, int flags[模式,O_RDONLY / O_WRONLY / O_RDWR / O_CREAT / O_APPEND], mode_t mode[文件权限]);
系统接口:open a file or device
O_RDONLY / O_WRONLY / O_RDWR / O_CREAT / O_APPEND:这些对应的是宏,具体就是一串二进制序列,只有一个为1,这样就可以实现用一个数字同时传递多个选项(通过按位或的方式);
int fd = open("log.txt", O_WRONLY);如果“log.txt”这个文件不存在,那么就会打开失败(返回“-1”)不会自行创建;int fd = open("log.txt", O_WRONLY | O_CREAT);这样就会自行创建了;但是这样创建出来的文件的权限不会是你想要的,如果想要创建想要的文件权限,就要用上“mode”这个参数;int fd = open("log.txt", O_WRONLY | O_CREAT,0666);这样就可以自行设置创建文件的权限了;
但上述文件权限设置方法,会被权限掩码“umask”过滤,如果不想要设置的权限被掩码过滤,可以使用
#include <sys/types.h> #include <sys/stat.h> mode_t umask(mode_t mode)
修改当前进程的文件权限掩码,umask(0);
open()的返回值,fd(int)是文件描述符;
#include <unistd.h>
ssize_t write(int fd, const void* buf, size_t count);
- 系统接口:write to a file descriptor;
- 示例:const char* message = "hello file system call \n"; write(fd, message, strlen(message));
- 此时的写入是从文件的起点开始写,但并不会清空原文件的内容;如果想要清空原文件,需要在open()函数的flag参数再加上O_TRUNC;同理,如果想要在后面加上内容就用“O_APPEND” ;
#include <unistd.h>
ssize_t read(int fd, const void* buf, size_t count);
- 系统接口:read from a file descriptor;
- count是缓冲区的大小;
#include <unistd.h>
int close(int fildes);
- 系统接口:close a file descriptor;
三、访问文件的本质
操作系统会使用“struct file”描述一个被打开文件的信息,以便进行管理,类似于进程的PCB;该结构体直接或间接包含:文件路径、文件基本属性(权限、大小、读写位置、打开用户)、文件的内核缓冲区信息、struct file* next;
进程PCB中会有一个“struct file_struct* files”的指针,它指向一个包含指针数组的结构体(文件描述符表),该数组中的指针指向所有被该进程打开的文件的文件描述结构体“struct file”,而上述系统接口中的返回值“int fd”就是这个数组的下标;该数组元素第0,1,2个指针默认指向标准输入/输出/错误流;
C语言里面的“FILE”是C库自己封装的结构体,因为操作系统进程访问文件是通过文件描述符“int fd”的,因此可以肯定FILE结构体里包含了fd(FILE->_fileno);
一个文件可以被多个进程打开,因此struct file结构体里会包含一个叫“引用计数count”的字段;进程关闭文件,只会将文件的count减1,并将文件描述符表中的对应指针置空,如果count不为0就不需要其它操作了,如果count为0系统才需要回收资源;