文件系统中dentry和inode的主要作用

上一篇文章中我们简单讨论了struct super_block的作用, 我们知道在struct super_block中可以找到我们文件系统的根目录, 有了这个根目录我们就可以在下面新建文件了, 但是我们没有具体讨论这个根目录是如何生成的, 因为要用到dentryinode的概念, 今天我们将继续这个讨论

目录和文件

我们知道, 在Linux系统中几乎一切都是文件

inode的作用就是在文件系统上代表一个文件, 不论这个文件有多大, 也不论这个文件是什么类型

但是inode中并没有存放文件的名字, 实际上文件的名字是存放在一个特殊的文件里的, 这个特殊的文件就是目录(没错, 目录也是一个文件), 目录中的每一个条目都指向一个文件, 这个条目我们称之为目录项(dentry)

dentrystruct dentry

在Linux内核中我们使用struct dentry来代表一个目录项

struct dentry {
    struct inode *d_inode;   /* 目录项对应的 inode */
    struct dentry *d_parent; /* 父目录的目录项 */
    struct qstr d_name;      /* 目录项的名称 */
    unsigned int d_count;    /* 目录项的引用计数 */
    // ... 其他成员变量
};

其中d_name就是inode的名字(也就是文件名), d_inode就是该目录项关联的inode

下图可以大概描述dentryinode的关系

+---------------+       +-----------------+
|               |       |                 |
|     inode     |<---   |      dentry     |
|               |   |   |                 |
|---------------|   |   |-----------------|
| i_mode        |   |   | d_name  --------|> inode指向的文件的名字
| i_uid         |   |--<|-d_inode         |
| i_gid         |       | d_parent -------|> 父目录的Dentry对象
| i_size        |       | ...             |
| ...           |       |                 |
+---------------+       +-----------------+

可以简单理解为, inode指向一个具体的文件, 而dentry又指向这个inode, 所以从dentry出发就可以找到任何一个文件, 实际上内核对dentry做了一系列优化, 使得查找文件的过程非常快

inodestruct inode

在Linux内核中我们使用struct inode来代表文件系统上的inode

struct inode {
    umode_t i_mode;          /* 文件类型和访问权限 */
    uid_t i_uid;             /* 用户ID */
    gid_t i_gid;             /* 组ID */
    dev_t i_rdev;            /* 设备号 */
    loff_t i_size;           /* 文件大小 */
    struct timespec i_atime; /* 最后访问时间 */
    struct timespec i_mtime; /* 最后修改时间 */
    struct timespec i_ctime; /* 创建时间 */
    unsigned long i_ino;     /* 索引节点号 */
    unsigned int i_count;    /* 索引节点的引用计数 */
    // ... 其他成员变量
};

正如上面所说, inode用来代表一个文件/目录, 每个文件或目录都有一个唯一的inode,它保存了有关该文件或目录的所有元数据信息,例如创建时间、修改时间、大小、所属用户和组等。此外,inode还存储有关文件或目录的物理位置、文件类型和权限等信息。当我们读取或写入文件时,内核会使用inode来定位并操作实际的文件内容。

文件系统根目录的是如何生成的

我们知道在struct super_block中存放了该文件系统的根目录(s_root字段), 现在我们可以讨论这个目录是如何生成的了

简单起见我们使用ramfs为例看看这个根目录是如何生成的

ramfs也叫内存文件系统, 应该是Linux内核中最简单的文件系统了, 这个文件系统中的所有文件都保存在内存里, 无需写入磁盘, 所以实现非常简单, 用这个文件系统作为例子, 来理解文件系统的基本逻辑非常合适

ramfs中, 根目录的生成是在ramfs_fill_super()函数中, 代码如下, 为了方便理解我删除了无关紧要的代码

int ramfs_fill_super(struct super_block *sb, void *data, int silent)
{
    // ...其他代码
    
    // 1. 获取根目录的inode
	inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0);
    // 2. 生成根目录的dentry
	sb->s_root = d_make_root(inode);
    
    // ...其他代码
	return 0;
}

下面我们分别看看这2个关键步骤

ramfs_get_inode

ramfs_get_inode代码如下, 为了方便理解我删除了无关紧要的代码

struct inode *ramfs_get_inode(struct super_block *sb,
				const struct inode *dir, umode_t mode, dev_t dev)
{
    // new一个inode
	struct inode * inode = new_inode(sb);
	
	// ...其他代码
	
	// 填充操作函数
	inode->i_op = &ramfs_dir_inode_operations;
	inode->i_fop = &simple_dir_operations;
	
	// ...其他代码
	
	return inode;
}

由于ramfs是内存文件系统, 不需要从磁盘读取inode信息, 这里直接new_inode()在内存里分配了一个inode, 然后设置了inode的操作函数, 就完成了

d_make_root

d_make_root代码如下, 为了方便理解我删除了无关紧要的代码

struct dentry *d_make_root(struct inode *root_inode)
{
    // new一个dentry
	struct dentry *res = __d_alloc(root_inode->i_sb, NULL);
	
	// 把root_inode关联到我们new的dentry
	d_instantiate(res, root_inode);
	
	return res;
}

这里的逻辑也很简, 先new一个dentry, 然后把根目录的inode和这个dentry关联起来就完成了, 当然具体是如何关联的还有一些细节问题, 但是这里我们不讨论, 有兴趣的读者可以在参考资料中看到这些细节

至此, 根目录就创建完成了, 有了根目录我们就也在这个文件系统上创建文件了, 我们将在后续的文章中讨论创建文件的过程

参考资料

struct dentry 结构体
struct inode 结构体
https://elixir.bootlin.com/linux/v4.8/source/fs/ramfs
https://elixir.bootlin.com/linux/v4.8/source/fs/ext2/super.c
https://blog.51cto.com/liangchaoxi/5422262
https://elixir.bootlin.com/linux/v4.8/source/fs/dcache.c#L1846

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值