参考以下两篇文章
http://blog.csdn.net/yuzhihui_no1/article/details/51272563
http://blog.csdn.net/shanshanpt/article/details/39026915
VFS虚拟文件系统,是对于用户的接口,它允许硬盘中有多种文件系统,而VFS屏蔽了底层的差异,使得对于上层看来底层是一样的。
在Linux的task_struct中,里面和文件相关的有两个指针,先说下其中的files,它指向struct file_struct结构体,这个结构体是每个进程独自拥有的。下面是相关的一些内容
struct files_struct {
atomic_t count; /* 共享该表的进程数 */
rwlock_t file_lock; /* 保护以下的所有域,以免在tsk->alloc_lock中的嵌套*/
int max_fds; /*当前文件对象的最大数*/
int max_fdset; /*当前文件描述符的最大数*/
int next_fd; /*已分配的文件描述符加1*/
struct file ** fd; /* 指向文件对象指针数组的指针 */
fd_set *close_on_exec; /*指向执行exec( )时需要关闭的文件描述符*/
fd_set *open_fds; /*指向打开文件描述符的指针*/
fd_set close_on_exec_init;/* 执行exec( )时需要关闭的文件描述符的初 值集合*/
fd_set open_fds_init; /*文件描述符的初值集合*/
struct file * fd_array[32];/* 文件对象指针的初始化数组*/
};
要解释的是其中的struct file **fd,fd也是指向一张表,表中存放的是指针,指向struct file,它初始化的时候是等于struct file fd_array[32]中的fd_array,这个是表只能存下32个struct file指针,当打开的文件多于32的时候,内核会将fd指向另一张表,并且将原来表中的内容复制过去。并且更新max_fds。我们每次用open打开一个文件,返回一个整数,就是作为fd的偏移量,去查找相关的struct file指针。open_fds是指向打开的文件描述符,它是指向fd_set结构的(fd_set是位,里面的每一位代表一个数字)。它初始化的时候是指向open_fds_init,比如0,1,2这三个一开始就是打开的描述符,fd_array[0]是指向标准输出的指针。之后每次打开一个文件内核就会将文件描述符添加到里面open_fds指向的fd_set数据结构。另外关于select中描述符最大是1024,可以看出fd_set这个数据结构本身受限制。
在讲解下面的几个结构体之前,先说下List_head结构体,内核中大量用到这个结构体,详细参考这篇文章http://blog.csdn.net/u013904227/article/details/50931540
内核中将节点连接成链表,是在节点中存放struct list_head{ struct list_head *next,*pre} 这样一个结构体。通过这个结构体将节点连接成双向链表。之后根据这个链表中的这个节点可以找到此节点所在的结构体。
索引节点Inode:inode是保存在磁盘中,它存放的内容的文件的一些元数据,如文件大小,访问时间,用户标识符,设备标识符,链接的数量,以及指向实际的数据块的指针。inode在硬盘中的数量是一定的。操作系统会把硬盘中的inode分为使用和未使用保存在内存中。一个文件对应一个inode,一个Inode也只标记一个文件,文件的名字是给用户使用的,在操作系统的内部用的是Inode。
struct inode {
441 struct list_head i_hash;
442 struct list_head i_list;
443 struct list_head i_dentry;
444
445 struct list_head i_dirty_buffers;
446 struct list_head i_dirty_data_buffers;
447
448 unsigned long i_ino;
449 atomic_t i_count;
450 kdev_t i_dev;
451 umode_t i_mode;
452 unsigned int i_nlink;
453 uid_t i_uid;
454 gid_t i_gid;
455 kdev_t i_rdev;
456 loff_t i_size;
457 time_t i_atime;
458 time_t i_mtime;
459 time_t i_ctime;
460 unsigned int i_blkbits;
461 unsigned long i_blksize;
462 unsigned long i_blocks;
463 unsigned long i_version;
464 unsigned short i_bytes;
465 struct semaphore i_sem;
466 struct rw_semaphore i_alloc_sem;
467 struct semaphore i_zombie;
468 struct inode_operations *i_op;
469 struct file_operations *i_fop; /* former ->i_op->default_file_ops */
470 struct super_block *i_sb;
471 wait_queue_head_t i_wait;
472 struct file_lock *i_flock;
473 struct address_space *i_mapping;
474 struct address_space i_data;
475 struct dquot *i_dquot[MAXQUOTAS];
476 /* These three should probably be a union */
477 struct list_head i_devices;
478 struct pipe_inode_info *i_pipe;
479 struct block_device *i_bdev;
480 struct char_device *i_cdev;
481
482 unsigned long i_dnotify_mask; /* Directory notify events */
483 struct dnotify_struct *i_dnotify; /* for directory notifications */
484
485 unsigned long i_state;
486
487 unsigned int i_flags;
488 unsigned char i_sock;
489
490 atomic_t i_writecount;
491 unsigned int i_attr_flags;
492 __u32 i_generation;
493 union {
494 struct minix_inode_info minix_i;
495 struct ext2_inode_info ext2_i;
496 struct ext3_inode_info ext3_i;
497 struct hpfs_inode_info hpfs_i;
498 struct ntfs_inode_info ntfs_i;
499 struct msdos_inode_info msdos_i;
500 struct umsdos_inode_info umsdos_i;
501 struct iso_inode_info isofs_i;
502 struct nfs_inode_info nfs_i;
503 struct sysv_inode_info sysv_i;
504 struct affs_inode_info affs_i;
505 struct ufs_inode_info ufs_i;
506 struct efs_inode_info efs_i;
507 struct romfs_inode_info romfs_i;
508 struct shmem_inode_info shmem_i;
509 struct coda_inode_info coda_i;
510 struct smb_inode_info smbfs_i;
511 struct hfs_inode_info hfs_i;
512 struct adfs_inode_info adfs_i;
513 struct qnx4_inode_info qnx4_i;
514 struct reiserfs_inode_info reiserfs_i;
515 struct bfs_inode_info bfs_i;
516 struct udf_inode_info udf_i;
517 struct ncp_inode_info ncpfs_i;
518 struct proc_inode_info proc_i;
519 struct socket socket_i;
520 struct usbdev_inode_info usbdev_i;
521 struct jffs2_inode_info jffs2_i;
522 void *generic_ip;
523 } u;
524 };</span></pre><span style="font-size:14px"><br>
i_dentry:指向目录项链表的指针。对于一个inode可以对应对个dentry(dentry下面会说),也就是硬链接-----一个实际的文件可以连接到其他的文件中去。每一个文件都对应一个dentry。这个指针可以将相同的inode的dentry连接起来。Linux里面有查看一个文件所以的硬链接,就是和这个有关。
i_hash是为了加快正在使用的Inode的查找效率,将正在使用的inode放在哈希表中,这个是开哈希表。根据不同的Inode值映射到数组上面,在将哈希值相等的inode通过i_hash连接起来。
struct dentry 目录项结构体 ,目录项值存在内存中,为了提高查找文件的性能设计的。注意在VFS里面无论是文件还是目录,都看成文件。d_inode指向目录的inode,下面的d_hash和上面的inode的i_hash差不多,也是开哈希表。d_subdirs,目录中的子项都要链接到这个d_subdirs的链表中,子项是通过d_child链接到d_subdirs。
struct file结构体:
structfile
{
struct list_head f_list; /*所有打开的文件形成一个链表*/
struct dentry *f_dentry; /*指向相关目录项的指针*/
struct vfsmount *f_vfsmnt; /*指向VFS安装点的指针*/
struct file_operations *f_op; /*指向文件操作表的指针*/
mode_t f_mode; /*文件的打开模式*/
loff_t f_pos; /*文件的当前位置*/
unsigned short f_flags; /*打开文件时所指定的标志*/
unsigned short f_count; /*使用该结构的进程数*/
unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
/*预读标志、要预读的最多页面数、上次预读后的文件指针、预读的字节数以及
预读的页面数*/
int f_owner; /* 通过信号进行异步I/O数据的传送*/
unsigned int f_uid, f_gid; /*用户的UID和GID*/
int f_error; /*网络写操作的错误码*/
unsigned long f_version; /*版本号*/
void *private_data; /* tty驱动程序所需 */
};
这个表示操作系统维护的,所有进程共享的。每次打开一个文件都会建立这个结构的(即使在一个进程中重复打开一个文件也会重新建立一个此文件的struct file的结构的),f_list使得这些结构连接起来。f_dentry指向此文件对应的f_dentry,一个文件在内存中会对应一个dentry的,这个下面会讲。f_op是指向文件操作表的指针,文件操作表里面存放的是read,write等等和读取文件的系统调用,文件存放在不同的文件系统中,这个变量指向的表也不一样。f_count是引用计数,当f_count为0,那么这个文件才会被关闭。f_pos是读写的位置。当一个进程,fork出子进程后,子进程持有父进程struct file_struct的一块拷贝,并且持有fd指向的文件描述符表的拷贝。这样父进程对文件的位置的操作(例如read,write)对于子进程来说也是可见的。
通过上面的我们用open打开一个文件的操作是这样子的,open返回一个文件描述符,通过文件描述符找到struct file,在这个里面找到dentry,根据计算的hash值,在i_hash链接成的表中找到次节点,在根据节点中的inode值,找到对应的block,如果block中存放的是数据,那就是我们要找到的文件,如果存放的是子项和inode的映射,那么继续往下找,知道找到文件为止。