fd:文件的操作句柄
文件描述符:非负整数(内核中文件描述信息结构体数组的下标)
进程通过 struct file 结构体来描述打开的文件,使用了 struct file *fd_array[]; 文件描述符就是这个数组的下标;用户打开文件,操作系统通过 struct file 结构体描述文件,并且将指针添加进 fd_array 数组中,向用户返回这个文件描述信息在数组中位置(下标),用户操作文件的时候,将这个下标传递给操作系统,操作系统通过下标找到文件描述信息进而操作文件。
为什么打开一个文件后,如果不操作了一定要关闭,释放资源? 文件描述符实际是有限的,若不关闭文件,文件描述符用完了后,则在进程中就打不开新文件了。
一个程序运行起来后,进程中默认打开三个文件:标准输入stdin(0),标准输出stdout(1),标准错误stderr(2)
标准输入标准输出标准错误012文件描述符(int)stdinstdoutstderr文件流指针(FILE*)测试用例:
//这个demo体会关闭1号文件描述符,即标准输出 #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> int main() { //将文件权限掩码设置为0 umask(0); //关闭标准输出文件描述符 close(1); int fd = open("./test.txt", O_RDWR | O_CREAT, 0664); if(fd < 0) { perror("open error"); return -1; } printf("fd = %d\n", fd); fflush(stdout); //关闭文件,释放资源 close(fd); return 0; }从上述例子我们可以发现 printf 并非真的一定要把数据写入标准输出文件,而是因为 printf 函数中操作文件的时候操作的描述符是1。原本 向1中写入数据就是向标准输出写入,然后当1指向了 新的文件后,这时候 printf 就会将数据写入到指定的新的文件中。
重定向 将数据不再写入原本的文件,而是写入新的指定的文件中,实现方式就是替换这个描述符对应的文件描述信息。实际是描述符的重定向,改变描述符所指向的文件,就改变了数据的流向。
描述符重定向函数: 让 newfd 这个描述符也指向 oldfd 所指向的文件,这时候 oldfd 和 newfd 都能够操作 oldfd 所指向的文件。
测试用例:
//这个demo体会关闭1号文件描述符,即标准输出 #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> int main() { //将文件权限掩码设置为0 umask(0); int fd = open("./test_dup2.txt", O_RDWR | O_CREAT, 0664); if(fd < 0) { perror("open error"); return -1; } //将1号文件描述符也指向fd所描述的文件 dup2(fd, 1); printf("fd = %d\n", fd); fflush(stdout); //关闭文件,释放资源 close(fd); return 0; }库函数是对系统调用接口的一层封装,通过文件流指针进行最终文件操作的时候,依然还要能够找到文件对应的文件描述符才可以 。文件流指针是一个结构体,结构体中有很多成员变量,其中就有一个叫 _fileno ,这就是文件描述符。 库函数接口向文件中写入数据,并不会直接写入文件,而是先写入缓冲区中,刷新缓冲区的时候才会写入文件。 系统调用接口是直接将数据写入文件的,系统调用接口没有这个缓冲区的。只有库函数才存在这个缓冲区。
测试用例:
//这个demo体会库函数接口和系统调用接口 缓冲区的有无及输出 #include <stdio.h> #include <unistd.h> #include <fcntl.h> int main() { //库函数接口必须刷新缓冲区后才会将数据打印,程序退出前会刷新缓冲区,所以最后打印 fprintf(stdout, "%s---%d", "fprintf", 1); fwrite("fwrite", 1, 6, stdout); printf("printf"); //系统调用接口直接将数据写入到文件中,所以先打印 write(1, "write", 5); sleep(3); return 0; }Linux下的 ext2 文件系统为例。文件系统就是磁盘上管理文件的系统。 存储一个文件需要找到空闲的磁盘块存储文件数据,以及需要找到一个未被使用的 inode 节点存储自己的元信息。 针对每一个磁盘块做一个整体的位图,这样就可以快速找到空闲的磁盘块。
文件的权限就是使用位图存储,一个用户没有权限也就是 0/1 的关系,位图可以节省大量的空间以及二进制的与或非速度非常快。
文件的存储过程:通过inode_bitmap在inode区域获取空闲inode节点,通过data_bitmap获取空闲数据块,在inode结点中记录文件信息以及数据块位置,并且将文件数据写入到数据块中,将自己的目录项信息(inode节点号+文件名)添加到所在目录文件中。
目录文件:一个文件–>文件中记录的目录下的文件信息(文件名+inode节点号)–>目录项
文件的查找过程:通过目录项信息快速找到文件的inode节点号,通过inode节点号可以在inode table中快速找到文件的inode节点;通过inode节点可以找到文件数据存储位置,进而获取到数据。
给一个源文件创建一个软链接文件/硬链接文件,就可以通过被创建出来的软链接文件/硬链接文件来操作源文件
为源文件创建一个硬链接文件:ln failname.txt failname.hard 为源文件创建一个软链接文件:ln -s failname.txt failname.soft
软链接文件和硬链接文件的区别 : 硬链接文件:本质上和源文件没有什么不同,都是一个文件的名称,与源文件共用同一个 inode节点,通过自己的 inode节点访问源文件数据。 软链接文件:本质上是一个独立的文件,有自己的 inode节点,文件数据中保存源文件的路径,通过这个路径访问源文件的数据。
删除上的区别: 删除源文件,软链接失效,硬链接文件只是链接数 -1,链接数(一个 inode节点对应有几个目录项) 删除一个文件,文件并不会立即被删除,而是直接删除了目录项信息,inode中的链接数 -1,此后链接数为 0 时,才会真正删除文件
软链接文件可以跨分区,硬链接文件不可以 软链接文件可以对目录创建,硬链接不可以