分类: LINUX
2013-10-22 21:57:12
Linux下的文件系统中宏观上主要分为三层:一是上层的文件系统的系统调用;二是虚拟文件系统VFS(Virtual File System)层,三是挂载到VFS中的各种实际文件系统。
VFS是一种软件机制,只存在于内存中,每次系统初始化期间Linux都会先在内存中构造一棵VFS的目录树(也就是源码中的namespace)。VFS主要的作用是对上层应用屏蔽底层不同的调用方法,提供一套统一的调用接口,二是便于对不同的文件系统进行组织管理。因此,VFS其实就是文件系统组织管理中的一个抽象层。
一个典型的VFS目录组织方式如下图所示:
Superblock:超级块,是文件系统最基本的元数据,它定义了文件系统的类型、大小、状态和其他信息等。Superblock对于文件系统是非常关键的,因此一般文件系统都会冗余存储多份。
struct super_block {
struct list_head s_list; /* 系统将所有文件系统的超级块组成链表*/
dev_t s_dev; /* search index; _not_ kdev_t */
unsigned long s_blocksize;
unsigned char s_blocksize_bits;
unsigned char s_dirt;
unsigned long long s_maxbytes; /* Max file size */
struct file_system_type *s_type; //文件系统类型
const struct super_operations *s_op; //操作函数集
struct dquot_operations *dq_op;
struct quotactl_ops *s_qcop;
struct export_operations *s_export_op;
unsigned long s_flags;
unsigned long s_magic;
struct dentry *s_root; //挂载根目录
struct rw_semaphore s_umount;
struct mutex s_lock;
int s_count;
int s_syncing;
int s_need_sync_fs;
atomic_t s_active;
#ifdef CONFIG_SECURITY
void *s_security; //LSM框架的安全域
#endif
struct xattr_handler **s_xattr;
struct list_head s_inodes; /* 所有的inode节点链表*/
struct list_head s_dirty; /* dirty inodes */
struct list_head s_io; /* parked for writeback */
struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */
struct list_head s_files;
struct block_device *s_bdev;
struct list_head s_instances;
struct quota_info s_dquot; /* Diskquota specific options */
int s_frozen;
wait_queue_head_t s_wait_unfrozen;
char s_id[32]; /* Informational name */
void *s_fs_info; /* Filesystem private info */
/*
* The next field is for VFS *only*. No filesystems have any business
* even looking at it. You had been warned.
*/
struct mutex s_vfs_rename_mutex; /* Kludge */
/* Granularity of c/m/atime in ns.
Cannot be worse than a second */
u32 s_time_gran;
};
inode:包含了一个文件的元数据,如文件所在数据块等。这里需要注意Linux中的所有对象均为文件:实际的文件、目录、设备等等。一个inode基本包含:所有权(用户、组)、访问模式(读、写、执行)和文件类型等,但不包含文件名。
struct inode {
struct hlist_node i_hash;
struct list_head i_list;
struct list_head i_sb_list; //链入超级块中的链表
struct list_head i_dentry;
unsigned long i_ino; //节点号
atomic_t i_count; //引用计数
unsigned int i_nlink; //硬链接数目
uid_t i_uid; //表示文件所有者
gid_t i_gid; //表示文件所有者所属组
dev_t i_rdev;
unsigned long i_version;
loff_t i_size;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
unsigned int i_blkbits;
blkcnt_t i_blocks;
unsigned short i_bytes;
umode_t i_mode; //inode权限
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem;
const struct inode_operations *i_op; //inode操作函数集
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct super_block *i_sb; //指向超级块
struct file_lock *i_flock;
struct address_space *i_mapping;
struct address_space i_data;
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];
#endif
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
int i_cindex;
__u32 i_generation;
#ifdef CONFIG_DNOTIFY
unsigned long i_dnotify_mask; /* Directory notify events */
struct dnotify_struct *i_dnotify; /* for directory notifications */
#endif
#ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct mutex inotify_mutex; /* protects the watches list */
#endif
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned int i_flags;
atomic_t i_writecount;
#ifdef CONFIG_SECURITY
void *i_security; //LSM框架中的安全域
#endif
void *i_private; /* fs or device private pointer */
};
inode结构中的i_ino索引节点号在同一个文件系统中是唯一的,但若是系统中挂载了多个文件系统,则inode号可能出现相同的情况。
其中这个 i_mode是16位的无符号整数表示,由9位权限方式位、3位“粘滞”标志位和4位文件类型标志位,它们的具体的定义在 include/linux/stat.h中:
#define S_IFMT 00170000 /* 用于抽取i_mode域中类型部分的屏蔽位 */
#define S_IFSOCK 0140000 /* 套接字类型码 */
#define S_IFLNK 0120000 /* 符号连接类型码 */
#define S_IFREG 0100000 /* 普通文件类型码 */
#define S_IFBLK 0060000 /* 块特别文件类型码 */
#define S_IFDIR 0040000 /* 目录文件类型码 */
#define S_IFCHR 0020000 /* 字符特别文件类型码 */
#define S_IFIFO 0010000 /* 管道或FIFO类型码 */
#define S_ISUID 0004000 /* 用户粘滞位 */
#define S_ISGID 0002000 /* 用户组粘滞位 */
#define S_ISVTX 0001000 /* 粘滞位 */
#define S_IRWXU 00700 /* 用户读写执行 */
#define S_IRUSR 00400 /* 用户读 */
#define S_IWUSR 00200 /* 用户写 */
#define S_IXUSR 00100 /* 用户执行 */
#define S_IRWXG 00070 /* 用户组读写执行 */
#define S_IRGRP 00040 /* 用户组读 */
#define S_IWGRP 00020 /* 用户组写 */
#define S_IXGRP 00010 /* 用户组执行 */
#define S_IRWXO 00007 /* 其他用户读写执行 */
#define S_IROTH 00004 /* 其他用户读 */
#define S_IWOTH 00002 /* 其他用户写 */
#define S_IXOTH 00001 /* 其他用户执行 */
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) /* 全部用户读写执行 */
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO )/* 全部用户全部权限 */
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) /* 全部用户读 */
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) /* 全部用户写 */
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) /* 全部用户执行 */
dentry:将inode号和文件名联系起来,dentry还保存目录和其子对象的关系,用于文件系统的变量。dentry还起着缓存的作用,缓存最常使用的文件以便于更快捷的访问。
struct dentry {
atomic_t d_count; //引用计数
unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name; //目录项名称,与inode对应起来
struct list_head d_lru; /* LRU list */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */ 链入父目录的子目录中
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children */ 子目录链表
struct list_head d_alias; /* inode alias list */ 链入inode节点的i_dentry链表,因为一个inode可能对应多个dentry(硬链接)
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
void *d_fsdata; /* fs-specific data */
#ifdef CONFIG_PROFILING
struct dcookie_struct *d_cookie; /* cookie, if any */
#endif
int d_mounted;
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};
在内存中,每个文件都有一个dentry(目录项)和inode(索引节点)结构,dentry记录文件名、父目录、子目录等信息,形成文件树结构。inode结构则包含文件的宿主、创建时间和在存储介质上的位置和分布等信息。其中每个dentry都有一个唯一的inode,而每个inode则可能有多个dentry,这种情况是由ln硬链接产生的。
硬链接:其实就是同一个文件具有多个别名,具有相同inode,而dentry不同。
1. 文件具有相同的inode和data block;
2. 只能对已存在的文件进行创建;
3. 不同交叉文件系统进行硬链接的创建
4. 不能对目录进行创建,只能对文件创建硬链接
5. 删除一个硬链接并不影响其他具有相同inode号的文件;
软链接:软链接具有自己的inode,即具有自己的文件,只是这个文件中存放的内容是另一个文件的路径名。因此软链接具有自己的inode号以及用户数据块。
1. 软链接有自己的文件属性及权限等;
2. 软链接可以对不存在的文件或目录创建;
3. 软链接可以交叉文件系统;
4. 软链接可以对文件或目录创建;
5. 创建软链接时,链接计数i_nlink不会增加;
6. 删除软链接不会影响被指向的文件,但若指向的原文件被删除,则成死链接,但重新创建指向 的路径即可恢复为正常的软链接,只是源文件的内容可能变了。
file:每一个打开的文件都对应一个file结构,还保存文件的打开方式和偏移等。
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;
unsigned long f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
注意:VFS文件系统中的inode和dentry与实际文件系统的inode和dentry有一定的关系,但不能等同。真实磁盘文件的inode和dentry是存在于物理外存上的,但VFS中的inode和dentry是存在于内存中的,系统读取外存中的inode和dentry信息进行一定加工后,生成内存中的inode和dentry。虚拟的文件系统也具有inode和dentry结构,只是这是系统根据相应的规则生成的,不存在于实际外存中。
对应外存上的实际文件系统在格式化时就划分出了inode的数目和数据区块的数量,所以有可能inode用完了但数据区块还有剩余空间的情况。即虽然磁盘仍有剩余空间,但不能再创建文件和目录。