522
京东网上商城
linux文件系统(二)——VFS四个主要对象的实现
声明:本Linux文件系统博客,共分四节,是根据网上多个相关博客,以及自己的理解加上相关资料总结而成。(作者:lvyilong316)
VFS采用的是面向对象的设计思想,使用一簇数据结构来代表通用文件对象。所有内核中的数据结构都使用C结构体实现。
1.superblock(超级块)对象
保存一个挂在的文件系统的相关信息(Stores information concerning a mounted filesystem. For disk-based filesystems, this object usually corresponds to a filesystem control block stored on disk.)
(1)超级块用来描述特定文件系统的信息。它存放在磁盘特定的扇区中 ,它在使用的时候将信息存在于内存中。
(2)当内核对一个文件系统进行初始化和注册时在内存为其分配一个超级块,这就是VFS超级块。即,VFS超级块是各种具体文件系统在安装时建立的,并在这些文件系统卸载时被自动删除 。
1.1 数据结构
(3)超级块对象由结构体 super_block来体现。VFS超级块的数据结构为 super_block在include/linux/fs.h中可以查看
struct super_block {
…….
};
我们先来看一个图,再来具体解释:
其中主要的数据成员和解释如下:
(1) s_list :所有的超级块形成一个双联表,s_list.prev和s_list.next分别指向与当前超级块相邻的前一个元素和后一个元素。
(2) s_lock :保护链表免受多处理器系统上的同时访问。
(3) s_fs_info: 字段指向具体文件系统的超级块。
例如:超级块对象指的是Ext2文件系统,该字段就指向ext2_sb_info数据结构。
(4) s_dirt :来表示该超级块是否是脏的,也就是说,磁盘上的数据是否必须要更新。
(5) 超级块对象是通过函数alloc_super()创建并初始化的。在文件系统安装时,内核会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存中的超级块对象中 。
1.2操作定义
超级对象中最重要的就是s_op,每一种文件系统都应该有自己的super_operations操作实例。它指向超级块的操作函数表, 它由struct super_operations结构体来表示。
现在来看一下它的定义:它的定义在 include/linux/fs.h头文件中可以看到
1560struct super_operations {
1561 struct inode *(*alloc_inode)(struct super_block *sb);
1562 void (*destroy_inode)(struct inode *);
1563
1564 void (*dirty_inode) (struct inode *);
1565 int (*write_inode) (struct inode *, struct writeback_control *wbc);
1566 int (*drop_inode) (struct inode *);
1567 void (*evict_inode) (struct inode *);
1568 void (*put_super) (struct super_block *);
1569 void (*write_super) (struct super_block *);
1570 int (*sync_fs)(struct super_block *sb, int wait);
1571 int (*freeze_fs) (struct super_block *);
1572 int (*unfreeze_fs) (struct super_block *);
1573 int (*statfs) (struct dentry *, struct kstatfs *);
1574 int (*remount_fs) (struct super_block *, int *, char *);
1575 void (*umount_begin) (struct super_block *);
1576
1577 int (*show_options)(struct seq_file *, struct vfsmount *);
1578 int (*show_stats)(struct seq_file *, struct vfsmount *);
1579#ifdef CONFIG_QUOTA
1580 ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
1581 ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
1582#endif
1583 int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
1584};
(1) 可以看到该结构体中的每一项都是一个指向超级块操作函数的指针,超级块操作函数执行文件系统和索引节点的底层操作。
(2)当文件系统需要对超级块执行操作时,要在超级块对象中寻找需要的操作方法。
例如:一个文件系统要写自己的超级块,需要调用:
sturct super_block * sb;
sb->s_op->write_super(sb);
sb是指向文件系统超级块的指针,沿着该指针进入超级块操作函数表,并从表中取得writ_super()函数,该函数执行写入超级块的实际操作。
说明:
尽管writ_super()方法来自超级块,但是在调用时,还是要把超级块作为参数传递给它。因为没有C++的this指针。
(3)具体操作说明
struct inode * alloc_inode(struct super_block * sb) :创建和初始化一个新的索引结点。
void destroy_inode(struct super_block *sb) :释放指定的索引结点 。
void dirty_inode(struct inode *inode) :VFS在索引节点被修改时会调用此函数。
void write_inode(struct inode *inode, struct writeback_control *wbc) 将指定的inode写回磁盘。
void drop_inode( struct inode * inode):删除索引节点。
void put_super(struct super_block *sb) :用来释放超级块。
void write_super(struct super_block *sb):更新磁盘上的超级块。
void sync_fs(struct super_block *sb,in wait):使文件系统的数据元素与磁盘上的文件系统同步,wait参数指定操作是否同步。
int statfs(struct super_block *sb,struct statfs *statfs):获取文件系统状态。把文件系统相关的统计信息放在statfs中。
2.VFS的索引节点(ls查看的信息)
(文件或目录的静态描述信息,不随进程不同而变化)
(1) 保存一个文件的通用信息,每个inode有一个inode number,在文件系统中,一个inode number能够唯一地标识一个文件(Stores general information about a specific file. For disk-based filesystems, this object usually corresponds to a file control block stored on disk. Each inode object is associated with an inode number, which uniquely identifies the file within the filesystem.)
(2) 文件系统处理文件或目录时的所有信息都存放在称为索引节点的数据结构中。文件名可以随时改,但是索引节点对文件是唯一的(它是随文件的存在而存在)。
(3) 具体文件系统的索引节点是存放在磁盘上的,是一种静态结构,要使用它,必须将其调入内存,填写 VFS的索引节点。VFS索引节点也称为动态节点。(即索引节点仅当文件被访问时才在内存中创建)
2.1数据结构
它的定义在 /include/linux/fs.h中有这个结构体的定义
struct inode {
……
};
还记的我们在终端下输入命令:ls 命令后可以看到文件的信息,这些信息就是记录在这里的。这是为什么呢?
我们知道,文件是由FCB(文件控制块控制的),而具体到Linux下,文件是有索引节点结构控制的。所以在struct inode 里存放了文件的基本信息。大家有没有发现在怎么在索引节点里面会包含超级块的对象呢,有些人可能不明白了,先看看下面的图,再来解释把。
从上面对的图我们可以看出索引节点 对象靠i_sb指回到了超级块对象。
成员说明:
i_hash :为了提高查找inode的效率,每一个inode都会有一个hash值。该字段指向hash值相同的inode所形成的双链表该字段包含prev和next两个指针,分别指向上述链表的前一个元素和后一个元素;
i_list :所有索引结点形成的双联表,(从图上可以看出,索引节点对象是靠它来链接的)
i_dentry :所有引用该inode的目录项将形成一个双联表,该字段即为这个双联表的头结点
i_ino :索引结点号。通过ls -i命令可以查看文件的索引节点号;
i_count :引用计数;
i_nlink :硬链接数。当该inode描述一个目录时,这个值至少为2,代表.和..的数目;
(注:索引节点没有软连接数,软连接会对应单独的索引节点)
i_uid :inode所属文件的拥有者的id,通过ls -n可查看拥有者id;
i_gid :inode所属文件所在组的id,通过ls -n可查看组id;
i_rdev :如果该inode描述的是一个设备文件,此值为设备号;
i_blkbits :以位为单位的块大小;
i_atime :文件最近一次被访问的时间。通过ls -lu可查看该时间;
i_mtime :文件最近一次被修改的时间,这里的修改只文件内容被修改。通过ls -l可查看该时间;
i_ctime :文件最近一次被修改的时间,这里的修改除了指文件内容被修改外,更强调的是文件的属性被修改。通过ls -lc可查看该时间;
i_blocks :文件使用块的个数,通过ls -s可以查看该某个文件的块使用数目;
i_mode :文件的访问权限;
i_op : 指向索引结点操作结构体的指针;
i_fop : 指向文件操作街头体的指针;
i_sb : 指向inode所属文件系统的超级块的指针;
i_pipe :如果inode所代表的文件是一个管道,则使用该字段;
i_bdev :如果inode所代表的文件是一个块设备,则使用该字段;
i_cdev :如果inode所代表的文件是一个字符设备,则使用该字段;
i_state : 索引节点的状态信息。
说明:
(1) 在同一个文件系统中,每个索引节点号都是唯一的,内核可以根据索引节点号的散列值来查找其inode结构。
(2) inode中有两个设备号i_dev和i_rdev。
a. 除特别文件外,每个节点都存储在某个设备上,这就是i_dev。
b. 如果索引节点所代表的并不是常规文件,而是某个设备,则需要另一个设备号,这就是i_rdev。
(3) 对i_state的说明:
每个VFS索引节点都会复制磁盘索引节点包含的一些数据,比如文件占有的磁盘数。如果i_state 的值等于I_DIR,该索引节点就是“脏“的。也就是说,对应的磁盘索引节点必须被更新。
(4) 三个重要的双向链表:
a.未用索引节点链表,正在使用索引节点链表和脏索引节点链表。每个索引节点对象总是出现在上面三种的一个。
b.这3个链表都是通过索引节点的i_list 域链接在一起的。
c.属于“正在使用“或“脏“链表的索引节点对象也同时存放在一个散列表中。
(4) 一个索引节点代表文件系统中的一个文件,它也可以是设备或管道这样的特殊文件。所以在索引节点结构体中有一些和特殊文件相关的项。
(6) 有时候某些文件系统并不能完整地包含索引节点结构体要求的所有信息。那么此时刚怎么办呢?
此时,可以给它赋一些其它的值。例如:一个文件系统可能并不记录文件的访问时间,这时就可以在i_atime中存储0。
(7) i_list和i_sb_list的区别
a.i_list:VFS中使用四个链表来管理不同状态的inode结点。inode_unused将当前未使用的inode链接起来,inode_in_use将当前正在被使用的inode链接起来,超级块中的s_dirty将所有脏inode链接起来,i_hash将所有hash值相同的inode链接起来。i_list中包含prev和next两个指针,分别指向与当前inode处于同一个状态链表的前后两个元素
b.i_sb_list:每个文件系统中的inode都会形成一个双联表,这个双链表的头结点存放在超级块的s_inodes中。而该字段中的prev和next指针分别指向在双链表中与其相邻的前后两个元素
c.索引结点中i_sb_list链表是链接一个文件系统中所有inode的链表,因此相邻的inode之间均会由此链表链接;而i_list链接的是处于同一个状态的所有inode。所以,相邻inode之间并不一定链接在一起。
2.2操作说明
与索引节点关联的方法叫索引节点操作表,它是在 struct inode_operations这个结构体中具体描述的。它的定义在 include/linux/fs.h头文件中定义。
1516struct inode_operations {
1517 int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
1518 struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
1519 int (*link) (struct dentry *,struct inode *,struct dentry *);
1520 int (*unlink) (struct inode *,struct dentry *);
1521 int (*symlink) (struct inode *,struct dentry *,const char *);
1522 int (*mkdir) (struct inode *,struct dentry *,int);
1523 int (*rmdir) (struct inode *,struct dentry *);
1524 int (*mknod) (struct inode *,struct dentry *,int,dev_t);
1525 int (*rename) (struct inode *, struct dentry *,
1526 struct inode *, struct dentry *);
1527 int (*readlink) (struct dentry *, char __user *,int);
1528 void * (*follow_link) (struct dentry *, struct nameidata *);
1529 void (*put_link) (struct dentry *, struct nameidata *, void *);
1530 void (*truncate) (struct inode *);
1531 int (*permission) (struct inode *, int);
1532 int (*check_acl)(struct inode *, int);
1533 int (*setattr) (struct dentry *, struct iattr *);
1534 int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
1535 int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
1536 ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
1537 ssize_t (*listxattr) (struct dentry *, char *, size_t);
1538 int (*removexattr) (struct dentry *, const char *);
1539 void (*truncate_range)(struct inode *, loff_t, loff_t);
1540 long (*fallocate)(struct inode *inode, int mode, loff_t offset,
1541 loff_t len);
1542 int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
1543 u64 len);
1544};
现在我们对其中一些重要的结果进行分析:
create() :如果该inode描述一个目录文件,那么当在该目录下创建或打开一个文件时,内核必须为这个文件创建一个inode。VFS通过调用该inode的i_op->create()函数来完成上述新inode的创建。该函数的第一个参数为该目录的 inode,第二个参数为要打开新文件的dentry,第三个参数是对该文件的访问权限。如果该inode描述的是一个普通文件,那么该inode永远都不会调用这个create函数;
lookup() :查找指定文件的dentry;
link :用于在指定目录下创建一个硬链接。这个link函数最终会被系统调用link()调用。该函数的第一个参数是原始文件的dentry,第二个参数即为上述指定目录的inode,第三个参数是链接文件的dentry。
symlink ():在某个目录下新建软连接(符号链接),第一个参数是原始文件所在目录的inode,第二个参数是原始文件的dentry,第三个参数是符号链接的名字(const char *)。
(关于硬链接,软连接与dentry和inode的关系以及的具体创建参考后面)
unlink ():在某个目录下删除dentry指定的索引节点对象。这个unlink函数最终会被系统调用unlink()调用。 第一个参数即为上述硬链接所在目录的inode,第二个参数为要删除文件的dentry。
(可以看到硬链接的创建就是dentry的创建,删除就是删除一个dentry)
mkdir:在指定的目录下创建一个子目录,当前目录的inode会调用i_op->mkdir()。该函数会被系统调用mkdir()调用。第一个参数即为指定目录的inode,第二个参数为子目录的dentry,第三个参数为子目录权限;(目录与子目录是通过目录inode中的dentry链相连的,而子目录的dentry又指向子目录自身的inode)
rmdir ():从inode所描述的目录中删除一个指定的子目录时,该函数会被系统调用rmdir()最终调用;
mknod() :在指定的目录下创建一个特殊文件,比如管道、设备文件或套接字等。
说明:
1. 对于不同的文件系统,上面的每个函数的具体实现是不同的,也不是每个函数都必须实现,没有实现的函数对应的域应当设置为NULL 。
2. 上面我们说了两个主要的操作对像:superblock和inode。它们两个对象中都包含一个操作对象。super_operations和inode_opetations它们有什么区别呢
a.super_operations对象:其中包括内核针对特定文件系统所有调用的方法。
b.inode_operations对象: 其中包括内核对特定文件的所有调用的方法。
所以它们一个是针对文件系统,一个是针对文件 。
3.本来inode中应该包括“目录节点”的名称,但由于硬链接的存在,导致一个物理文件可能有多个文件名,因此把和“目录节点”名称相关的部分从 inode 中分开,放在一个专门的 dentry 结构(目录项)中。
3.VFS 中的目录项对象
为了方便查找,VFS引入了目录项,每个dentry代表路径中的一个特定部分。目录项也可包括安装点。 可能还是不明白,没关系,看看老外怎么说的——保存一个目录的链接信息(Stores information about the linking of a directory entry (that is, a particular name of the file) with the corresponding file. Each disk-based filesystem stores this information in its own particular way on disk.)个人理解:描述一个文件和一个名字的对应关系,或者说dentry就是一个“文件名”。
3.1数据结构
目录项对象由dentry结构体表示 ,定义在文件linux/dcache.h 头文件中。
struct dentry {
……
};
说明:
1.索引节点中的i_dentry指向了它目录项,目录项中的d_alias,d_inode又指会了索引节点对象,目录项中的d_sb又指回了超级块对象。
2.我们可以看到不同于VFS 中的索引节点对象和超级块对象,目录项对象中没有对应磁盘的数据结构,所以说明目录项对象并没有真正标存在磁盘上,那么它也就没有脏标志位。
3.目录项的状态(被使用,未被使用和负状态)
它们是靠d_count的值来进行区分的,当d_count为正值说明目录项处于被使用状态。当d_count=0时表示该目录项是一个未被使用的目录项, 但其d_inode指针仍然指向相关的的索引节点。该目录项仍然包含有效的信息,只是当前没有人引用他。d_count=NULL表示负(negative)状态,与目录项相关的inode对象不复存在(相应的磁盘索引节点可能已经被删除),dentry对象的d_inode 指针为NULL。但这种dentry对象仍然保存在dcache中,以便后续对同一文件名的查找能够快速完成。这种dentry对象在回收内存时将首先被释放。
4. d_subdirs:如果当前目录项是一个目录,那么该目录下所有的子目录(一级子目录)形成一个链表。该字段是这个链表的表头;
5. d_child:如果当前目录项是一个目录,那么该目录项通过这个字段加入到父目录的d_subdirs链表当中。这个字段中的next和prev指针分别指向父目录中的另外两个子目录;
6. d_alias:一个inode可能对应多个目录项,所有的目录项形成一个链表。inode结构中的i_dentry即为这个链表的头结点。当前目录项以这个字段处于i_dentry链表中。该字段中的prev和next指针分别指向与该目录项同inode的其他两个(如果有的话)目录项。
3.2dentry和inode的区别
inode(可理解为ext2 inode)对应于物理磁盘上的具体对象,dentry是一个内存实体,其中的d_inode成员指向对应的inode。也就是说,一个inode可以在运行的时候链接多个dentry,而d_count记录了这个链接的数量。所谓"文件", 就是按一定的形式存储在介质上的信息,所以一个文件其实包含了两方面的信息,一是存储的数据本身,二是有关该文件的组织和管理的信息。在内存中, 每个文件都至少有一个dentry(目录项)和inode(索引节点)结构,dentry记录着文件名,上级目录等信息,正是它形成了我们所看到的树状结构;而有关该文件的组织和管理的信息主要存放inode里面,它记录着文件在存储介质上的位置与分布。同时dentry->d_inode指向相应的inode结构。dentry与inode是多对一的关系,因为有可能一个文件有好几个文件名(硬链接)。
3.3 操作定义
对目录项进行操作的一组函数叫目录项操作表,由dentry_operation结构描述。它可以在 include/linux/dcache.h 中查到
134struct dentry_operations {
135 int (*d_revalidate)(struct dentry *, struct nameidata *);
136 int (*d_hash) (struct dentry *, struct qstr *);
137 int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
138 int (*d_delete)(struct dentry *);
139 void (*d_release)(struct dentry *);
140 void (*d_iput)(struct dentry *, struct inode *);
141 char *(*d_dname)(struct dentry *, char *, int);
142};
(1) int d_reavlidate(struct dentry *dentry ,int flags) 该函数判断目录对象是否有效。VFS准备从dcache中使用一个目录项时,会调用该函数.
(2) int d_hash(struct dentry *dentry ,struct qstr *name):该目录生成散列值,当目录项要加入到散列表时,VFS要调用此函数。
(3) int d_compare( struct dentry *dentry, struct qstr *name1, struct qstr *name2) 该函数来比较name1和name2这两个文件名。使用该函数要加dcache_lock锁。
(4) int d_delete(struct dentry *dentry):当d_count=0时,VFS调用次函数。使用该函数要叫 dcache_lock锁。
(5) void d_release(struct dentry *dentry):当该目录对象将要被释放时,VFS调用该函数。
(6) void d_iput(struct dentry *dentry,struct inode *inode)当一个目录项丢失了其索引节点时,VFS就掉用该函数。
4.VFS中的文件对象 (与进程有关)
保存一个打开的文件与一个进程的关系(Stores information about the interaction between an open file and a process. This information exists only in kernel memory during the period when a process has the file open.)
1.文件对象表示进程已经打开的文件 在内存中的表示,该对象不是物理上的文件。它是由相应的open()系统调用创建,由close()系统调用销毁。多个进程可以打开和操作同一个文件,所以同一个文件也可能存在多个对应的文件对象。
2一个文件对应的文件对象不是唯一的,但对应的索引节点和超级块对象是唯一的。
3.file结构中保存了文件位置,此外,还把指向该文件索引节点的指针也放在其中。
4.1数据结构
file结构形成一个双链表,称为系统打开文件表 。它的定义在 include/linux/fs.h 中可以看到
struct file {
……
};
1.文件对象实际上没有对应的磁盘数据,所以在结构体中没有代表其对象是否为脏,是否需要写回磁盘的标志。文件对象通过f_path.dentry指针指向相关的目录项对象。目录项会指向相关的索引节点,索引节点会记录文件是否是脏的。
2.fu_list:每个文件系统中以被打开的文件都会形成一个双联表,这个双联表的头结点存放在超级块的s_files字段中。该字段的prev和next指针分别指向在链表中与当前文件结构体相邻的前后两个元素.
说明:
file结构中主要保存了文件当前的偏移量,此外还把指向该文件索引节点的指针也放在其中。有人就问了,问什么不直接把文件位置存放在索引节点中呢?
因为:Linux中的文件是能够共享的,假如把文件位置存放在索引节点中,当有两个或更多个进程同时打开一个文件时,它们将去访问同一个索引节点,那么一个进程的lseek操作将影响到另一个进程的读操作,这显然是致命的错误。
4.2 操作定义
对文件进行操作的一组函数叫文件操作表,由file_operations结构定义:可以在include/linux/fs.h 中查看
1488struct file_operations {
1489 struct module *owner;
1490 loff_t (*llseek) (struct file *, loff_t, int);
1491 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
1492 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
1493 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1494 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1495 int (*readdir) (struct file *, void *, filldir_t);
1496 unsigned int (*poll) (struct file *, struct poll_table_struct *);
1497 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
1498 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
1499 int (*mmap) (struct file *, struct vm_area_struct *);
1500 int (*open) (struct inode *, struct file *);
1501 int (*flush) (struct file *, fl_owner_t id);
1502 int (*release) (struct inode *, struct file *);
1503 int (*fsync) (struct file *, int datasync);
1504 int (*aio_fsync) (struct kiocb *, int datasync);
1505 int (*fasync) (int, struct file *, int);
1506 int (*lock) (struct file *, int, struct file_lock *);
1507 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
1508 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
1509 int (*check_flags)(int);
1510 int (*flock) (struct file *, int, struct file_lock *);
1511 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
1512 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
1513 int (*setlease)(struct file *, long, struct file_lock **);
1514};
(1) owner:用于指定拥有这个文件操作结构体的模块,通常取THIS_MODULE;
(2) llseek:用于设置文件的偏移量。第一个参数指明要操作的文件,第二个参数为偏移量,第三个参数为开始偏移的位置(可取SEEK_SET,SEEK_CUR和SEEK_END之一)。
(3) read:从文件中读数据。第一个参数为源文件,第二个参数为目的字符串,第三个参数指明欲读数据的总字节数,第四个参数指明从源文件的某个偏移量处开始读数据。由系统调用read()调用;
(4) write:往文件里写数据。第一个参数为目的文件,第二个参数源字符串,第三个参数指明欲写数据的总字节数,第四个参数指明从目的文件的某个偏移量出开始写数据。由系统调用write()调用;
(5) mmap:将指定文件映射到指定的地址空间上。由系统调用mmap()调用;
(6) open:打开指定文件,并且将这个文件和指定的索引结点关联起来。由系统调用open()调用;
(7) release:释放以打开的文件,当打开文件的引用计数(f_count)为0时,该函数被调用;
(8) fsync():文件在缓冲的数据写回磁盘
5.四大VFS对象总结
1、超级块对象和inode对象分别对应有物理数据,在磁盘上有静态信息。而目录项对象和文件对象描述的是一种关系,前者描述的文件与文件名的关系,后者描述的是进程与文件的关系,所以没有对应物理数据。
eg:有三个不同的进程打开同一个文件,其中有两个进程使用了相同的硬链接。三个进程拥有各自的file object,而只有两个dentry(同一个硬链接对应一个dentry,dentry不随进程打开文件而增加或改变)。两个dentry都指向同一个inode。
2、进程每打开一个文件,就会有一个file结构与之对应。同一个进程可以多次打开同一个文件而得到多个不同的file结构,file结构描述被打开文件的属性,如文件的当前偏移量等信息。
3、两个不同的file结构可以对应同一个dentry结构。进程多次打开同一个文件时,对应的只有一个dentry结构。
4、在存储介质中,每个文件对应唯一的inode结点,但是每个文件又可以有多个文件名。即可以通过不同的文件名访问同一个文件。这里多个文件名对应一个文件的关系在数据结构中表示就是dentry和inode的关系。
4)Inode中不存储文件的名字,它只存储节点号;而dentry则保存有名字和与其对应的节点号,所以就可以通过不同的dentry访问同一个inode。
5)不同的dentry则是同个文件链接(ln命令)来实现的。
最后更新:2017-04-03 12:55:35