Linux中文件描述符fd和文件指针flip的理解

本文深入解析Linux中的文件描述符fd和文件指针filp,阐述open与fopen的区别。介绍了VFS作为抽象层如何统一不同文件系统,以及在进程控制块PCB中的作用。详细讲解了file结构体、file_operations、dentry、inode和super_block,展示了文件操作在内核中的流程及重要数据结构。
摘要由CSDN通过智能技术生成

简单概括

fd只是一个整数,在open时产生。起到一个索引的做用,进程经过PCB中的文件描述符表找到该fd所指向的文件指针filp。

open和fopen

open:文件描述符的操做(如: open)返回的是一个文件描述符(int fd),内核会在每一个进程空间中维护一个文件描述符表, 全部打开的文件都将经过此表中的文件描述符来引用(fd1,fd2,fd3…);

fopen:而流(如: fopen)返回的是一个FILE结构指针, FILE结构是包含有文件描述符的,FILE结构函数能够看做是对fd直接操做的系统调用的封装, 它的优势是带有I/O缓存.node

VFS

Linux支持各类各样的文件系统格式,如ext2、ext3、reiserfs、FAT、NTFS、iso9660等等,不一样的磁盘分区、光盘或其它存储设备都有不一样的文件系统格式,然而这些文件系统均可以mount到某个目录下,使咱们看到一个统一的目录树,各类文件系统上的目录和文件咱们用ls命令看起来是同样的,读写操做用起来也都是同样的,这是怎么作到的呢?Linux内核在各类不一样的文件系统格式之上作了一个抽象层,使得文件、目录、读写访问等概念成为抽象层的概念,所以各类文件系统看起来用起来都同样(VFS做用),这个抽象层称为虚拟文件系统(VFS,Virtual Filesystem),这一节咱们介绍运行时文件系统在内核中的表示
在这里插入图片描述

进程控制块

每一个进程在PCB(Process Control Block)即进程控制块中都保存着一份文件描述符表,文件描述符就是这个表的索引,文件描述表中每一个表项都有一个指向已打开文件的指针,如今咱们明确一下:已打开的文件在内核中用file结构体表示,文件描述符表中的指针指向file结构体(理解:fd为打开文件的文件描述符,而每一个进程都有一张文件描述表,fd文件描述符就是这张表的索引,一样这张表中有一表项,该表项又是指向前面提到打开文件的file结构体,file结构体才是内核中用于描述文件属性的结构体)。

struct file-----define in inlcude/linux/fs.happ

 1 struct file {
 2     union {
 3         struct llist_node    fu_llist;
 4         struct rcu_head     fu_rcuhead;
 5     } f_u;
 6     struct path        f_path;
 7 #define f_dentry    f_path.dentry
 8     struct inode        *f_inode;    /* cached value */
 9     const struct file_operations    *f_op;
10 
11     /*
12      * Protects f_ep_links, f_flags.
13      * Must not be taken from IRQ context.
14      */
15     spinlock_t        f_lock;
16     atomic_long_t        f_count;
17     unsigned int         f_flags;
18     fmode_t            f_mode;
19     struct mutex        f_pos_lock;
20     loff_t            f_pos;
21     struct fown_struct    f_owner;
22     const struct cred    *f_cred;
23     struct file_ra_state    f_ra;
24 
25     u64            f_version;
26 #ifdef CONFIG_SECURITY
27     void            *f_security;
28 #endif
29     /* needed for tty driver, and maybe others */
30     void            *private_data;
31 
32 #ifdef CONFIG_EPOLL
33     /* Used by fs/eventpoll.c to link all the hooks to this file */
34     struct list_head    f_ep_links;
35     struct list_head    f_tfile_llink;
36 #endif /* #ifdef CONFIG_EPOLL */
37     struct address_space    *f_mapping;
38 #ifdef CONFIG_DEBUG_WRITECOUNT
39     unsigned long f_mnt_write_state;
40 #endif
41 } __attribute__((aligned(4)));    /* lest something weird decides that 2 is OK */

struct file_operations------define in include/linux/fs.hasync

 1 struct file_operations {
 2     struct module *owner;
 3     loff_t (*llseek) (struct file *, loff_t, int);
 4     ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 5     ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 6     ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 7     ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 8     int (*iterate) (struct file *, struct dir_context *);
 9     unsigned int (*poll) (struct file *, struct poll_table_struct *);
10     long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
11     long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
12     int (*mmap) (struct file *, struct vm_area_struct *);
13     int (*open) (struct inode *, struct file *);
14     int (*flush) (struct file *, fl_owner_t id);
15     int (*release) (struct inode *, struct file *);
16     int (*fsync) (struct file *, loff_t, loff_t, int datasync);
17     int (*aio_fsync) (struct kiocb *, int datasync);
18     int (*fasync) (int, struct file *, int);
19     int (*lock) (struct file *, int, struct file_lock *);
20     ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
21     unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
22     int (*check_flags)(int);
23     int (*flock) (struct file *, int, struct file_lock *);
24     ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
25     ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
26     int (*setlease)(struct file *, long, struct file_lock **);
27     long (*fallocate)(struct file *file, int mode, loff_t offset,
28               loff_t len);
29     int (*show_fdinfo)(struct seq_file *m, struct file *f);
30 };

file.File Status Flag和file.f_count

在file结构体中维护File Status Flag(file结构体的成员f_flags)和当前读写位置(file结构体的成员f_pos)。在上图中,进程1和进程2都打开同一文件,可是对应不一样的file结构体,所以能够有不一样的File Status Flag和读写位置。file结构体中比较重要的成员还有f_count,表示引用计数(Reference Count),后面咱们会讲到,dup、fork等系统调用会致使多个文件描述符指向同一个file结构体,例若有fd1和fd2都引用同一个file结构体,那么它的引用计数就是2,当close(fd1)时并不会释放file结构体,而只是把引用计数减到1,若是再close(fd2),引用计数就会减到0同时释放file结构体,这才真的关闭了文件。

file.file_operations

每一个file结构体都指向一个file_operations结构体,这个结构体的成员都是函数指针,指向实现各类文件操做的内核函数。好比在用户程序中read一个文件描述符,read经过系统调用进入内核,而后找到这个文件描述符所指向的file结构体,找到file结构体所指向的file_operations结构体,调用它的read成员所指向的内核函数以完成用户请求(应用层到内核层的调用流程)。在用户程序中调用lseek、read、write、ioctl、open等函数,最终都由内核调用file_operations的各成员所指向的内核函数完成用户请求。file_operations结构体中的release成员用于完成用户程序的close请求,之因此叫release而不叫close是由于它不必定真的关闭文件,而是减小引用计数,只有引用计数减到0才关闭文件。对于同一个文件系统上打开的常规文件来讲,read、write等文件操做的步骤和方法应该是同样的,调用的函数应该是相同的,因此图中的三个打开文件的file结构体指向同一个file_operations结构体。若是打开一个字符设备文件,那么它的read、write操做确定和常规文件不同,不是读写磁盘的数据块而是读写硬件设备,因此file结构体应该指向不一样的file_operations结构体(也就有了用户自定义结构体对象或者内核自定义结构体对象),其中的各类文件操做函数由该设备的驱动程序实现。

file.dentry

每一个file结构体都有一个指向dentry结构体的指针,“dentry”是directory entry(目录项)的缩写。咱们传给open、stat等函数的参数的是一个路径,例如/home/akaedu/a,须要根据路径找到文件的inode。为了减小读盘次数,内核缓存了目录的树状结构,称为dentry cache(做用),其中每一个节点是一个dentry结构体,只要沿着路径各部分的dentry搜索便可,从根目录/找到home目录,而后找到akaedu目录,而后找到文件a。dentry cache只保存最近访问过的目录项,若是要找的目录项在cache中没有,就要从磁盘读到内存中。

dentry.inode

每一个dentry结构体都有一个指针指向inode结构体。inode结构体保存着从磁盘inode读上来的信息。在上图的例子中,有两个dentry,分别表示/home/akaedu/a和/home/akaedu/b,它们都指向同一个inode,说明这两个文件互为硬连接。inode结构体中保存着从磁盘分区的inode读上来信息,例如全部者、文件大小、文件类型和权限位等(inode有哪些参数,正常理解file结构体可能包含这些信息,实际上是file.inode成员管理这些信息)。每一个inode结构体都有一个指向inode_operations结构体的指针,后者也是一组函数指针指向一些完成文件目录操做的内核函数。和file_operations不一样,inode_operations所指向的不是针对某一个文件进行操做的函数,而是影响文件和目录布局的函数,例如添加删除文件和目录、跟踪符号连接等等,属于同一文件系统的各inode结构体能够指向同一个inode_operations结构体。

inode.super_block

inode结构体有一个指向super_block结构体的指针。super_block结构体保存着从磁盘分区的超级块读上来的信息,例如文件系统类型、块大小等。super_block结构体的s_root成员是一个指向dentry的指针,表示这个文件系统的根目录被mount到哪里,在上图的例子中这个分区被mount到/home目录下。

总结

file、dentry、inode、super_block这几个结构体组成了VFS的核心概念。对于ext2文件系统来讲,在磁盘存储布局上也有inode和超级块的概念,因此很容易和VFS中的概念创建对应关系。而另一些文件系统格式来自非UNIX系统(例如Windows的FAT3二、NTFS),可能没有inode或超级块这样的概念,但为了能mount到Linux系统,也只好在驱动程序中硬凑一下,在Linux下看FAT32和NTFS分区会发现权限位是错的,全部文件都是rwxrwxrwx,由于它们原本就没有inode和权限位的概念,这是硬凑出来的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值