In the previous article, we briefly discussed the role of struct super_block
, we know that the root directory of our file system can be found in struct super_block
, with this root directory we You can create a new file below, but we did not discuss how the root directory is generated, because we need to use the concepts of dentry
and inode
, today we will continue this discuss
Directories and files
We know that almost everything in a Linux system is a file
The function of inode
is to represent a file on the file system, no matter how big the file is or what type it is
But the name of the file is not stored in the inode
. In fact, the name of the file is stored in a special file. This special file is the directory
(yes, the directory is also a file), each entry in directory
points to a file, and this entry is called dentry
dentry
and struct dentry
In the Linux kernel we use struct dentry
to represent a directory entry
struct dentry {<!-- --> struct inode *d_inode; /* inode corresponding to directory entry */ struct dentry *d_parent; /* the directory entry of the parent directory */ struct qstr d_name; /* the name of the directory entry */ unsigned int d_count; /* reference count of directory entry */ // ... other member variables };
Among them, d_name
is the name of inode
(that is, the file name), and d_inode
is the associated with the
directory item
>inode
The following figure can roughly describe the relationship between dentry
and inode
+ --------------- + + ----------------- + | | | | | inode |<--- | dentry | | | | | | |---------------| | |-----------------| | i_mode | | | d_name --------|> The name of the file pointed to by the inode | i_uid | |--<|-d_inode | | i_gid | | d_parent -------|> Dentry object of the parent directory | i_size | | ... | | ... | | | + --------------- + + ----------------- +
It can be simply understood as, inode
points to a specific file, and dentry
points to this inode
, so from dentry
You can find any file when you start. In fact, the kernel has made a series of optimizations to dentry
, making the process of finding files very fast
inode
and struct inode
In the Linux kernel we use struct inode
to represent inode
on the file system
struct inode {<!-- --> umode_t i_mode; /* file type and access rights */ uid_t i_uid; /* User ID */ gid_t i_gid; /* group ID */ dev_t i_rdev; /* device number */ loff_t i_size; /* file size */ struct timespec i_atime; /* last access time */ struct timespec i_mtime; /* last modification time */ struct timespec i_ctime; /* creation time */ unsigned long i_ino; /* inode number */ unsigned int i_count; /* reference count of index node */ // ... other member variables };
As mentioned above, inode
is used to represent a file/directory, and each file or directory has a unique inode
, which stores all information about the file or directory Metadata information, such as creation time, modification time, size, belonging user and group, etc. Additionally, an inode
stores information about the physical location of a file or directory, the file type, and permissions. When we read or write a file, the kernel uses inode
to locate and manipulate the actual file content.
How the root directory of the file system is generated
We know that the root directory of the file system (s_root
field) is stored in struct super_block
, now we can discuss how this directory is generated
For simplicity, let’s use ramfs
as an example to see how this root directory is generated
ramfs
is also called memory file system, it should be the simplest file system in Linux kernel, all files in this file system are stored in memory, no need to write to disk, so the implementation is very simple, use This file system is very suitable as an example to understand the basic logic of the file system
In ramfs
, the root directory is generated in the ramfs_fill_super() function, the code is as follows, for the convenience of understanding, I deleted the irrelevant code
int ramfs_fill_super(struct super_block *sb, void *data, int silent) {<!-- --> // ...other code // 1. Get the inode of the root directory inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0); // 2. Generate the dentry of the root directory sb->s_root = d_make_root(inode); // ...other code return 0; }
Let’s take a look at these two key steps
ramfs_get_inode
ramfs_get_inode
The code is as follows, for the convenience of understanding, I deleted the irrelevant code
struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev) {<!-- --> // new an inode struct inode * inode = new_inode(sb); \t // ...other code \t // fill operation function inode->i_op = & amp; ramfs_dir_inode_operations; inode->i_fop = &simple_dir_operations; \t // ...other code \t return inode; }
Since ramfs is a memory file system, there is no need to read inode
information from the disk. Here, new_inode()
allocates a inode
in the memory directly. Then set the operation function of inode
, and it’s done
d_make_root
The d_make_root
code is as follows, for the convenience of understanding, I deleted the irrelevant code
struct dentry *d_make_root(struct inode *root_inode) {<!-- --> // new a dentry struct dentry *res = __d_alloc(root_inode->i_sb, NULL); \t // Associate root_inode with our new dentry d_instantiate(res, root_inode); \t return res; }
The logic here is also very simple, first create a new dentry
, and then associate the inode
of the root directory with this dentry
to complete, of course the specific There are still some details about how it is related, but we will not discuss it here, interested readers can see these details in the reference materials
So far, the root directory has been created. With the root directory, we can also create files on this file system. We will discuss the process of creating files in subsequent articles
References
struct dentry structure
struct inode structure
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