Linux中文件描述符fd和文件指针flip的理解
简单概括
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和权限位的概念,这是硬凑出来的。