文件标识符fd
1.用c语言打开一个文件
#include<stdio.h> int main()
{ FILE* fp=fopen("log.txt","w"); if(fp==NULL) { perror("fopen"); return 1; } fclose(fp); return 0;
}
我们用“w"的方式打开log.txt文件,如果这个文件不存在,就创建这个文件,如果文件存在,就把这个文件内容清空然后打开
打开文件的本质是进程打开文件,文件没有被打开时,在磁盘中
一个进程可以打开多个文件,那么操作系统中肯定存在大量被打开的文件,那么怎么对这些文件管理呢?
2.文件管理
文件=属性+内容
文件->磁盘->外设->硬件,所以向文件写入本质上是向硬件中写入,但是用户们没有权力直接访问硬件,所以要通过操作系统访问硬件,我们用的C/C++文件操作本质上都是对系统调用接口的封装
1.系统调用的文件操作
#include<stdio.h> #include<unistd.h>#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int main() { umask(0);//修改文件权限默认掩码 //system call int fd=open("log.txt",O_WRONLY|O_CREAT,0666); if(fd<0) { perror("fd"); return 1; } close(fd); return 0; }
对于open函数
#include <fcntl.h>int open(const char *pathname, int flags, ...);
pathname:表示指向包含文件名的字符串指针,flags表示以不同的方式打开这个文件,比如:
O_RDONLY
:以只读方式打开文件。O_WRONLY
:以只写方式打开文件。O_RDWR
:以读写方式打开文件。O_CREAT
:如果文件不存在则创建它。O_TRUNC
:如果文件已存在且成功打开,则将其长度截断为 0。O_APPEND
:写入时将数据追加到文件末尾。O_EXCL
:与O_CREAT
一起使用,如果文件已存在则open
调用失败。O_NONBLOCK
:以非阻塞模式打开文件。O_SYNC
:所有写操作都应同步到存储设备。
...:如果我们第二个参数使用了O_CREAT,那么第三个参数表示创建文件的权限,它通常与umask相结合来确定文件的最终权限,比如umask为0,第三个参数设置成0666,表示文件权限是rw-rw-rw-,
open函数失败时返回-1,成功时返回一个非负的文件描述符;
2.fd理解
什么是fd呢?
文件描述符fd本质上是文件映射关系的下标
我们使用系统调用函数open打开文件时,首先我们把进程加载到内存中,进程的task_struct中有一个files指针,用来指向files_struct结构体,files_struct结构体是代表进程打开文件信息的数据结构,被打开的文件也会在内存中加载对应的file结构体,每一个被打开的文件在内存中有对应的struct file,同时开辟的有文件缓存区,然后文件内容从磁盘上拷贝到文件缓冲区中,然后被打开的文件属性保存到file结构体中,这样就可以对文件进行管理了,每一个file结构体对应的fd_array数组的下标,就是文件标识符fd,用来管理这个文件,然后我们获取到文件标识符fd,可以对文件进行写入,修改,
open的作用
- 创建file
- 开辟文件缓冲区的空间
- 查进程的文件标识符
- file地址,填入对应的表下标中
- 返回下标
3.fd的0,1,2
我们理解了,每一个文件标识符标识一个文件,那么fd 0 1 2 的文件默认被打开了,是什么文件呢?
0:标准输入 键盘
1:标准输出 显示器
2:标准错误 显示器
那么为什么这些硬件也叫做文件呢?如何理解linux一切皆文件
对于硬件,我们有对应的驱动管理,然后用虚拟文件管理封装成文件,就可以对文件进行管理了,
4.C语言的文件操作
所有c语言的文件操作函数,本质都是对系统调用的封装,
fclose,fopen,fread,fwrite这些库函数的类型都是FILE*,FILE是c语言提供的一个结构体类型,封装了fd,才可以对文件进行操作,
C语言为什么要封装呢?
因为不同系统,对应的系统调用接口也不同,那么写出的代码不具备跨平台性,而C语言想要具有跨平台性,就要对不同系统的系统函数进行封装,