【Linux】解锁文件形貌符奥秘,高效缓存区的实战本领 ...

打印 上一主题 下一主题

主题 808|帖子 808|积分 2424

1. 文件形貌符fd

1.1. 概念与本质


  • 定义:是用于标识打开文件的非负整数。
  • 文件形貌符的本质,就是数组下标。
1.2. 打开文件的管理

   问:为什么访问文件的体系调用接口,都必需使用文件形貌符fd?
  

  • 当我们打开一个文件时,OS会在内存中创建一个file结构体,用来形貌被打开的文件,这个结构体包罗了文件的当前读写位置、文件形貌符、文件路径等相干信息。
  1. struct file {
  2.     struct path f_path;          // 文件路径
  3.     struct file_operations *f_op; // 文件操作指针
  4.     int _fileno;                  //文件描述符
  5.     loff_t f_pos;                // 当前文件读写位置
  6.     ...
  7. };
  8. struct file_operations {
  9.     int (*open)(struct inode *, struct file *);
  10.     int (*release)(struct inode *, struct file *);
  11.     ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
  12.     ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
  13.     loff_t (*llseek)(struct file *, loff_t, int);
  14.     ...
  15. };
复制代码

  • 因为open函数是由进程来实行的,所以必须让进程和文件关联起来。在每一个task_struct结构体中都包罗了一个struct file_struct*类型的指针,它指向了一个包罗文件形貌符表的file_struct结构体。
  1. struct task_struct {
  2.     ...
  3.     files_struct *files;  // 文件描述符表
  4.     ...
  5. };
复制代码

  • 文件形貌符表是一个file*类型的指针数组,每个元素都指向一个打开的文件,而文件形貌符就是此数组的下标,所以只要拿到了文件形貌符表,就可以索引到对应的文件。
  1. struct files_struct {
  2.     ...
  3.     struct file *fd[FD_SETSIZE];  // 文件描述符数组
  4.     ...
  5. };
复制代码


打开文件本质:在内核中,把文件在磁盘上找到、内容和属性加载,在内存中创建file结构体,属性、方法、缓冲区都初始化,然后把结构体链入到体系管理文件的链表中,而且在指针数组中找到一个数据下标,再把它的地点填充进来,最后把数组下标(fd)返回给上层用户,应用层得到fd值。
a. 把数据写到文件中write:

  • 因为write是由进程通过体系调用来实行的,而体系可以或许辨认出是哪个进程在哀求服务,即:OS可以找到进程(task_struct);
  • 因为write需要访问文件,所以通过fd,直接在数组中举行索引,从而找到文件。再把用户空间中的缓冲区buf中的数据,拷贝到内核空间中的文件结构体对象的缓冲区中,最后让OS把缓冲区的刷新到磁盘文件中。
b. 从文件中读取数据read:

  • 因为read是由进程通过体系调用来实行的,而体系可以或许辨认出是哪个进程在哀求服务,即:OS可以找到进程(task_struct);
  • 因为read需要访问文件,所以通过fd,直接在数组中举行索引,从而找到文件。如果文件结构体对象的缓冲区中有内容,就直接读取到用户空间的缓冲区buf中,反之,就让OS把磁盘文件中的数据导入到内存中。
c. 关闭文件close:

  • 因为close是由进程通过体系调用来实行的,而体系可以或许辨认出是哪个进程在哀求服务,即:OS可以找到进程(task_struct);
  • 因为close需要访问文件,所以通过fd,直接在数组中举行索引,从而找到文件。OS再将文件结构体对象举行释放。
1.3. 统统皆文件的明确

一、文件体系的抽象和VFS

  • VFS(Virtual File System):虚拟文件体系,是Linux内核的一个软件层,它提供了一套同一的接口来访问各种类型的文件体系和硬件设备。
这种设计使得用户和应用程序可以或许通过调用雷同的体系调用(如open、write、read等)来操作不同的文件体系,而无需关心底层文件体系的具体实现细节。

  • 文件操作结构体file_operations:在linux内核中,每个打开的文件都有一个指向file_operations结构体的指针,它包罗一系列函数指针,即:它定义了文件的各种操作(如:读、写、打开、关闭),其内包罗的函数指针指向具体的实现方法。
不同的文件体系或硬件的驱动程序会提供这些函数的具体实现,但这些函数的参数类、返回值类型、函数名,必须与定义在file_operations结构体中的函数指针相匹配。
  1. struct file {
  2.     struct inode *f_inode;  // 文件的inode
  3.     struct file_operations *f_op;  // 文件操作函数指针
  4.     unsigned long f_flags;  // 文件标志
  5.     loff_t f_pos;  // 当前文件位置
  6.     // 其他信息...
  7. };
  8. struct file_operations {  //文件操作函数
  9.     int (*open)(struct inode *, struct file *);
  10.     int (*release)(struct inode *, struct file *);
  11.     ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
  12.     ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
  13.     loff_t (*llseek)(struct file *, loff_t, int);
  14.     // 其他操作...
  15. };
复制代码

  • VFS的焦点头脑是通过抽象和封装来屏蔽底层硬件的差异。
二、面向对象编程的类比

  • 多态性:在面向对象编程中,多态性答应我们使用同一的接口来调用不同的实现。在VFS中,是通过函数指针来实现多态性,不同的文件体系的具体实现方法不同,但上层应用程序只使用同一的函数指针接口。
  • 封装:VFS屏蔽了文件体系和硬件设备的差异,即:隐蔽了底层的细节;使得上层应用程序可以使用同一的函数指针接口来访问文件。
上层代码无需关心底层操作的实现,只需按照同一接口的规范举行操作。

不能从表现器读数据,平常在表现器输入的东西,表现器也表现了,不是通过表现器把数据交给了你的程序,而是从键盘中输入数据,你的程序先从键盘中读到的,为了让用户看到你输入的数据,程序就把数据同步的给表现器拷贝了一份。
1.4. 分配规则


  • fd分配规则:最小未被使用的数组下标,会被分配给最新打开的文件。
  1. #include<stdio.h>
  2. #include<sys/types.h>
  3. #include<fcntl.h>
  4. #include<unistd.h>
  5. #include<sys/stat.h>
  6. #include<string.h>
  7.   
  8. int main()
  9. {
  10.     close(1);//关闭标准输出流
  11.     int fd1 =  open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
  12.     printf("fd1: %d\n", fd1); //printf默认向stdin—>fd = 1打印
  13.     printf("hello world\n");  //输出重定向:本来应该把内容向显示器文件进行写入,更改为向磁盘文件进行写入
  14.     return 0;
  15. }
复制代码

  1. #include<stdio.h>
  2. #include<sys/types.h>
  3. #include<fcntl.h>
  4. #include<unistd.h>
  5. #include<sys/stat.h>
  6. #include<string.h>
  7.   
  8. int main()
  9. {
  10.     close(0);  //关闭标入输出流                                          
  11.     int fd2 = open("data.txt", O_RDWR);
  12.     printf("fd2: %d\n", fd2);
  13.     char buff[64];
  14.     fgets(buff, 64, stdin);   //输入重定向:本来应该从键盘文件中读取的内容,更改为从磁盘文件中读取
  15.     printf("%s\n", buff);
  16.    
  17.     return 0;
  18. }
复制代码

1.5. 重定向的本质


  • 重定向的本质:更改文件形貌符表的内容,即:更改文件形貌符(stdin、stdou、stderr)的指向,使得本来要写入到标准输出的数据,被重定向到其他文件、大概本来要从标准输入中读取的数据,重定向到来自于其他文件。

1.5.1. dup2

   int dup2(int oldfd,int newfd);
  

  • 功能:将stdin、stdout、stderr重定向到文件或其他设备。
  • 参数:oldfd:要被复制的文件形貌符;newfd:目标文件的形貌符。
  • 返回值:成功,返回新的文件形貌符(即:newfd)。堕落时,返回 -1,并设置 errno以指示错误。

  1. #include<stdio.h>
  2. #include<sys/types.h>
  3. #include<fcntl.h>
  4. #include<unistd.h>
  5. #include<sys/stat.h>
  6. int main()
  7. {
  8.     int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
  9.     dup2(fd, 1);
  10.     printf("hello zzx\n");
  11.     return 0;
  12. }
复制代码

2. FILE中的缓冲区

2.1. 概念


  • 概念:本质是一块内存地区,用于暂时存放数据,以便更高效地处理输入、输出操作。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户国营

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表