招远做网站公司,什么服装网站做一件代发,朝阳做网站,做鞋子出口需要作网站吗文章目录 前言一、简介二、iget_locked2.1 简介2.2 内核中使用2.3 LKM demo 三、ext4_iget3.1 简介3.2 LKM demo 前言
文件inode号和struct inode结构体请参考#xff1a; Linux文件路径#xff0c;目录项#xff0c;inode号关联 Linux文件系统 struct inode 结构体解析
一… 文章目录 前言一、简介二、iget_locked2.1 简介2.2 内核中使用2.3 LKM demo 三、ext4_iget3.1 简介3.2 LKM demo 前言
文件inode号和struct inode结构体请参考 Linux文件路径目录项inode号关联 Linux文件系统 struct inode 结构体解析
一、简介
在Linux中每个文件和目录都与一个唯一的inode号相关联。inode号是文件系统中inode的唯一标识符用于表示文件或目录的元数据。
这就意味inode号在文件系统中是不可以重复的。
在同一个文件系统中文件的inode号是唯一的不会重复。每个文件系统维护着一个独立的inode表其中的inode号在该文件系统中是唯一的。
然而不同文件系统之间的inode号可以重复。当不同的文件系统挂载到同一个系统上时它们各自维护着独立的inode号空间。因此不同文件系统中的文件可以拥有相同的inode号但它们在各自的文件系统中是唯一的。
这意味着如果在不同文件系统中有两个文件的inode号是相同的那么它们实际上是不同的文件属于不同的文件系统。
需要注意的是文件系统的实现可能会限制inode号的范围或分配策略具体取决于文件系统的设计和实现。
因此当涉及到多个文件系统时不同文件系统中的inode号确实可以重复。这是因为每个文件系统都有自己的inode号空间用于在该文件系统中唯一标识文件和目录。
当不同的文件系统挂载到同一个操作系统中时每个文件系统会独立地管理自己的inode号。这意味着即使两个文件系统中存在具有相同inode号的文件它们仍然被视为不同的文件并且彼此之间没有任何关联。
这种情况可能发生在以下情况下 1多个独立的物理磁盘或分区每个磁盘或分区上的文件系统都有自己的inode号空间它们彼此独立。
2文件系统镜像如果相同的文件系统映像被多次挂载每次挂载都会创建一个独立的inode号空间。
3网络文件系统NFS当使用NFS挂载远程文件系统时本地文件系统和远程文件系统的inode号彼此独立。
这种设计允许不同的文件系统在同一个操作系统中并存并通过挂载点进行访问。每个文件系统都有自己的inode号空间确保了在各自范围内的唯一性。
需要注意的是尽管不同文件系统中的inode号可以重复但通常并不鼓励依赖inode号来唯一标识文件。在跨文件系统或跨系统的环境中使用文件路径或其他唯一标识符更可靠和可移植。
二、iget_locked
接下来介绍根据文件系统即超级块 文件inode编号来获取 struct inode结构体对象流程图如下所示
2.1 简介
通常前面的知识我们可以知道不同文件系统中的文件可以拥有相同的inode号它们在各自的文件系统中是唯一的因此我们根据在内核态编程中根据文件inode号获取其对应的struct inode结构体那么需要明确文件系统和inode号才能获取其对应的struct inode结构体不能单独通过inode号获取其struct inode结构体可以使用iget_locked函数。
/*** iget_locked - obtain an inode from a mounted file system* sb: super block of file system* ino: inode number to get** Search for the inode specified by ino in the inode cache and if present* return it with an increased reference count. This is for file systems* where the inode number is sufficient for unique identification of an inode.** If the inode is not in cache, allocate a new inode and return it locked,* hashed, and with the I_NEW flag set. The file system gets to fill it in* before unlocking it via unlock_new_inode().*/
struct inode *iget_locked(struct super_block *sb, unsigned long ino)
{struct hlist_head *head inode_hashtable hash(sb, ino);struct inode *inode;
again:spin_lock(inode_hash_lock);inode find_inode_fast(sb, head, ino);spin_unlock(inode_hash_lock);if (inode) {if (IS_ERR(inode))return NULL;wait_on_inode(inode);if (unlikely(inode_unhashed(inode))) {iput(inode);goto again;}return inode;}inode alloc_inode(sb);if (inode) {struct inode *old;spin_lock(inode_hash_lock);/* We released the lock, so.. */old find_inode_fast(sb, head, ino);if (!old) {inode-i_ino ino;spin_lock(inode-i_lock);inode-i_state I_NEW;hlist_add_head(inode-i_hash, head);spin_unlock(inode-i_lock);inode_sb_list_add(inode);spin_unlock(inode_hash_lock);/* Return the locked inode with I_NEW set, the* caller is responsible for filling in the contents*/return inode;}/** Uhhuh, somebody else created the same inode under* us. Use the old inode instead of the one we just* allocated.*/spin_unlock(inode_hash_lock);destroy_inode(inode);if (IS_ERR(old))return NULL;inode old;wait_on_inode(inode);if (unlikely(inode_unhashed(inode))) {iput(inode);goto again;}}return inode;
}
EXPORT_SYMBOL(iget_locked);iget_locked 函数通常在文件系统中使用用于从已挂载的文件系统中获取一个 inode索引节点。
iget_locked 函数根据inode编号和超级块快速访问inode对象这两项的组合在系统范围内是唯一的。
该函数的实现过程如下 首先根据给定的 inode 号 ino 和文件系统的超级块 sb使用 hash 函数计算哈希值。该哈希值用于定位 inode 缓存中相应的哈希表条目。
函数获取自旋锁 inode_hash_lock以在执行必要的操作时锁定 inode 缓存。
它搜索哈希链表由 hlist_head 指针 head 指示以查找与给定的 inode 号 ino 相关联的 inode。它使用 find_inode_fast 函数进行搜索。
如果在缓存中找到了 inode函数通过使用 IS_ERR 函数检查它是否是有效的 inode。如果该 inode 无效则返回 NULL。否则它使用 wait_on_inode 函数等待 inode 上的任何挂起操作完成。
如果发现 inode 是未哈希的即不在哈希表中它将释放该 inode并回到开始处重新搜索。
如果在缓存中未找到该 inode则继续使用 alloc_inode 函数分配一个新的 inode。这将创建一个新的 struct inode 数据结构并进行初始化。
分配完 inode 后函数释放自旋锁并重新获取它以执行另一次搜索以查找该 inode。这是必要的因为在 alloc_inode 调用期间释放了锁而另一个线程可能在此期间将该 inode 添加到缓存中。
如果没有其他线程添加了相同 inode 号的 inode将设置新分配的 inode 的字段。将 i_ino 字段设置为给定的 inode 号 ino将 i_state 字段设置为 I_NEW以表示该 inode 是新的且尚未完全初始化然后使用 hlist_add_head 将 inode 添加到哈希表中。使用 inode_sb_list_add 将 inode 添加到超级块的 inode 列表中。
然后释放自旋锁和 inode 缓存锁并将带有设置了 I_NEW 标志的锁定 inode 返回给调用者。调用者负责填充 inode 的内容。
如果在自旋锁释放期间有其他线程添加了相同 inode 号的 inode则函数选择使用现有的 inode 而不是新分配的 inode。它使用 wait_on_inode 等待现有 inode 上的任何挂起操作完成。如果现有 inode 是未哈希的则释放它并回到开始处重新搜索。
最后函数将获得的 inode无论是新分配的还是现有的返回给调用者。
/** find_inode_fast is the fast path version of find_inode, see the comment at* iget_locked for details.*/
static struct inode *find_inode_fast(struct super_block *sb,struct hlist_head *head, unsigned long ino)
{struct inode *inode NULL;repeat:hlist_for_each_entry(inode, head, i_hash) {if (inode-i_ino ! ino)continue;if (inode-i_sb ! sb)continue;spin_lock(inode-i_lock);if (inode-i_state (I_FREEING|I_WILL_FREE)) {__wait_on_freeing_inode(inode);goto repeat;}if (unlikely(inode-i_state I_CREATING)) {spin_unlock(inode-i_lock);return ERR_PTR(-ESTALE);}__iget(inode);spin_unlock(inode-i_lock);return inode;}return NULL;
}该函数的作用是在给定的哈希链表 head 中快速查找与给定 inode 号 ino 和超级块 sb 相匹配的 inode。
函数使用 hlist_for_each_entry 宏遍历哈希链表中的每个 inode。对于每个 inode它首先检查 inode 号和超级块是否与给定的匹配如果不匹配则继续遍历下一个 inode。
如果找到匹配的 inode函数将获取 inode 的自旋锁然后进行一系列的检查 ● 首先它检查 inode 的状态是否处于 I_FREEING 或 I_WILL_FREE 状态这表示 inode 正在释放或即将被释放。如果是这种情况函数将调用 __wait_on_freeing_inode 函数等待 inode 完全释放然后重新从头开始查找。
● 接下来函数检查 inode 的状态是否处于 I_CREATING 状态这表示 inode 正在创建过程中。如果是这种情况函数将释放 inode 的自旋锁并返回一个指向 -ESTALE 错误的指针表示 inode 已过期。
● 最后如果 inode 的状态正常函数将调用 __iget 函数增加 inode 的引用计数然后释放 inode 的自旋锁并返回指向该 inode 的指针。
如果在整个哈希链表中没有找到匹配的 inode函数将返回 NULL。
这个函数用于在inode缓存中快速查找 inode以加速文件系统中对 inode 的访问操作。
2.2 内核中使用
这个函数一般是具体的文件系统函数调用根据文件inode号和超级块从inode缓存中获取其对应的struct inode结构体如果没有从inode缓存中找到那么具体的文件系统函数会调用自己的文件系统获取函数获取磁盘上的原始inode结构体比如ext4 inode然后用ext4 inode的值来初始化struct inode结构体对象然后将struct inode结构体对象加入到inode高速缓存中以便下次加速查找。
比如minix文件系统
/** The global function to read an inode.*/
struct inode *minix_iget(struct super_block *sb, unsigned long ino)
{struct inode *inode;inode iget_locked(sb, ino);if (!inode)return ERR_PTR(-ENOMEM);if (!(inode-i_state I_NEW))return inode;if (INODE_VERSION(inode) MINIX_V1)return V1_minix_iget(inode);elsereturn V2_minix_iget(inode);
}首先调用iget_locked函数根据文件inode号和超级块从inode缓存中获取其对应的struct inode结构体加速struct inode结构体的查找如果没有在缓存中找到那么便分配一个struct inode结构体结构体调用具体文件系统的函数V1_minix_iget读取minix文件系统中的minix_inode即磁盘上的minix inode结构体用磁盘上的minix inode结构体成员来初始化struct inode结构体对象将struct inode结构体对象加入到inode缓存中以便下次加速查找。
/** The minix V1 function to read an inode.*/
static struct inode *V1_minix_iget(struct inode *inode)
{struct buffer_head * bh;struct minix_inode * raw_inode;struct minix_inode_info *minix_inode minix_i(inode);int i;//根据超级块和inode编号从minix磁盘文件系统获取磁盘上的inode节点minix_inoderaw_inode minix_V1_raw_inode(inode-i_sb, inode-i_ino, bh);if (!raw_inode) {iget_failed(inode);return ERR_PTR(-EIO);}//根据磁盘上的inode节点minix_inode 来初始化 struct inode结构体对象inode-i_mode raw_inode-i_mode;i_uid_write(inode, raw_inode-i_uid);i_gid_write(inode, raw_inode-i_gid);set_nlink(inode, raw_inode-i_nlinks);inode-i_size raw_inode-i_size;inode-i_mtime.tv_sec inode-i_atime.tv_sec inode-i_ctime.tv_sec raw_inode-i_time;inode-i_mtime.tv_nsec 0;inode-i_atime.tv_nsec 0;inode-i_ctime.tv_nsec 0;inode-i_blocks 0;for (i 0; i 9; i)minix_inode-u.i1_data[i] raw_inode-i_zone[i];minix_set_inode(inode, old_decode_dev(raw_inode-i_zone[0]));brelse(bh);unlock_new_inode(inode);return inode;
}这段代码是用于在Minix V1文件系统中读取一个inode的函数。
函数解析 调用minix_V1_raw_inode函数通过给定的super_block、inode号以及一个用于传递缓冲区头指针的指针获取Minix V1版本的原始inode结构minix_inode。
将获取的原始inode结构minix_inode中的字段赋值给目标inode结构。这包括inode的模式i_mode、用户IDi_uid、组IDi_gid、链接数i_nlinks、文件大小i_size、修改时间i_mtime、访问时间i_atime、创建时间i_ctime等。
将原始inode结构minix_inode中的数据块地址赋值给目标inode结构的数据块地址。在Minix V1文件系统中数据块地址存储在minix_inode结构的u.i1_data数组中。
使用minix_set_inode函数将目标inode结构中的设备号设置为原始inode结构中的第一个数据块地址。
返回struct inode结构体对象。
比如ext2文件系统
struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
{struct ext2_inode_info *ei;struct buffer_head * bh NULL;struct ext2_inode *raw_inode;struct inode *inode;long ret -EIO;int n;uid_t i_uid;gid_t i_gid;inode iget_locked(sb, ino);if (!inode)return ERR_PTR(-ENOMEM);if (!(inode-i_state I_NEW))return inode;ei EXT2_I(inode);ei-i_block_alloc_info NULL;raw_inode ext2_get_inode(inode-i_sb, ino, bh);if (IS_ERR(raw_inode)) {ret PTR_ERR(raw_inode);goto bad_inode;}inode-i_mode le16_to_cpu(raw_inode-i_mode);i_uid (uid_t)le16_to_cpu(raw_inode-i_uid_low);i_gid (gid_t)le16_to_cpu(raw_inode-i_gid_low);if (!(test_opt (inode-i_sb, NO_UID32))) {i_uid | le16_to_cpu(raw_inode-i_uid_high) 16;i_gid | le16_to_cpu(raw_inode-i_gid_high) 16;}i_uid_write(inode, i_uid);i_gid_write(inode, i_gid);set_nlink(inode, le16_to_cpu(raw_inode-i_links_count));inode-i_size le32_to_cpu(raw_inode-i_size);inode-i_atime.tv_sec (signed)le32_to_cpu(raw_inode-i_atime);inode-i_ctime.tv_sec (signed)le32_to_cpu(raw_inode-i_ctime);inode-i_mtime.tv_sec (signed)le32_to_cpu(raw_inode-i_mtime);inode-i_atime.tv_nsec inode-i_mtime.tv_nsec inode-i_ctime.tv_nsec 0;......
}2.3 LKM demo
接下来我们给出一个LKM示例根据inode号来获取对应的struct inode结构体
#include linux/init.h
#include linux/kernel.h
#include linux/module.h#include linux/fs.h
#include linux/fs_struct.h/* Module parameter */
static unsigned long ino 1837047;module_param(ino, ulong, 0);static int __init hello_init(void)
{struct fs_struct *fs;struct path pwd;//unsigned long ino 1837047;struct inode *inode;struct super_block *sb;fs current-fs;get_fs_pwd(fs, pwd);/* The root of the dentry tree */sb pwd.dentry-d_sb;//从inode 缓存中查找 struct inode 对象inode iget_locked(sb, ino);if(inode)printk(inode num %ld\n, inode-i_ino);return -1;
}module_init(hello_init);MODULE_LICENSE(GPL);ls -il 3.txt
1837252# insmod hello.ko ino1837252
insmod: ERROR: could not insert module hello.ko: Operation not permitted# dmesg -c
inode num 1837252这只是从inode缓存中查找struct inode如果inode缓存中没有那么便要去磁盘中查找对应的磁盘inode结构体。
三、ext4_iget
3.1 简介
我们简单看一下ext4文件系统根据inode号获取其struct inode结构体的方式
// linux-5.4.18/fs/ext4/ext4.htypedef enum {EXT4_IGET_NORMAL 0,EXT4_IGET_SPECIAL 0x0001, /* OK to iget a system inode */EXT4_IGET_HANDLE 0x0002 /* Inode # is from a handle */
} ext4_iget_flags;extern struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,ext4_iget_flags flags, const char *function,unsigned int line);#define ext4_iget(sb, ino, flags) \__ext4_iget((sb), (ino), (flags), __func__, __LINE__)ext4文件系统通过ext4_iget宏来获取struct inode结构体宏定义 ext4_iget调用 __ext4_iget 函数来获取指定inode号对应的 struct inode 结构体。
该宏定义接受三个参数
sb指向 super_block 结构体的指针表示要操作的超级块。 ino表示要获取的inode号。 flags表示 ext4_iget_flags 类型的标志位用于指定 __ext4_iget 函数的行为。
宏定义将参数传递给 __ext4_iget 函数并在最后两个参数 func 和 LINE 中传递了调用该宏的函数名和行号。这些信息通常用于调试目的以便在发生错误时能够追踪到具体的调用位置。
对于第三个参数flags
typedef enum {EXT4_IGET_NORMAL 0,EXT4_IGET_SPECIAL 0x0001, /* OK to iget a system inode */EXT4_IGET_HANDLE 0x0002 /* Inode # is from a handle */
} ext4_iget_flags;枚举类型 ext4_iget_flags用于表示 ext4_iget 函数的标志或选项。
ext4_iget_flags 枚举类型包含三个成员
EXT4_IGET_NORMAL表示普通的 iget 操作。它用于指示 iget 函数应该获取普通文件或目录的 inode。
EXT4_IGET_SPECIAL表示可以 iget 系统级 inode。系统级 inode 是用于各种系统级操作或文件类型如超级块或日志文件的特殊目的 inode。
EXT4_IGET_HANDLE用于当 inode 号是从一个句柄handle中获取时。句柄是用于跨文件系统和进程访问 inode 的标识符通常用于实现文件系统的高级功能。
struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,ext4_iget_flags flags, const char *function,unsigned int line)
{struct ext4_iloc iloc;struct ext4_inode *raw_inode;struct ext4_inode_info *ei;struct inode *inode;journal_t *journal EXT4_SB(sb)-s_journal;long ret;loff_t size;int block;uid_t i_uid;gid_t i_gid;projid_t i_projid;......//从inode高速缓存中查找struct inodeinode iget_locked(sb, ino);if (!inode)return ERR_PTR(-ENOMEM);if (!(inode-i_state I_NEW))//在inode高速缓存中查找到并且不是最新分配的那么直接返回return inode;ei EXT4_I(inode);iloc.bh NULL;//在inode高速缓存中没有找到读取ext4文件系统的ext4_inoderet __ext4_get_inode_loc(inode, iloc, 0);if (ret 0)goto bad_inode;raw_inode ext4_raw_inode(iloc);//根据ext4文件系统的ext4_inode初始化新分配的struct inode//比如初始化i_mode、i_uid 、i_gid inode-i_mode le16_to_cpu(raw_inode-i_mode);i_uid (uid_t)le16_to_cpu(raw_inode-i_uid_low);i_gid (gid_t)le16_to_cpu(raw_inode-i_gid_low);......i_uid_write(inode, i_uid);i_gid_write(inode, i_gid); ......//根据inode的文件类型初始化 i_op 和 i_fop if (S_ISREG(inode-i_mode)) {inode-i_op ext4_file_inode_operations;inode-i_fop ext4_file_operations;ext4_set_aops(inode);} else if (S_ISDIR(inode-i_mode)) {inode-i_op ext4_dir_inode_operations;inode-i_fop ext4_dir_operations;} else if (S_ISLNK(inode-i_mode)) {/* VFS does not allow setting these so must be corruption */if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) {ext4_error_inode(inode, function, line, 0,iget: immutable or append flags not allowed on symlinks);ret -EFSCORRUPTED;goto bad_inode;}if (IS_ENCRYPTED(inode)) {inode-i_op ext4_encrypted_symlink_inode_operations;ext4_set_aops(inode);} else if (ext4_inode_is_fast_symlink(inode)) {inode-i_link (char *)ei-i_data;inode-i_op ext4_fast_symlink_inode_operations;nd_terminate_link(ei-i_data, inode-i_size,sizeof(ei-i_data) - 1);} else {inode-i_op ext4_symlink_inode_operations;ext4_set_aops(inode);}inode_nohighmem(inode);} else if (S_ISCHR(inode-i_mode) || S_ISBLK(inode-i_mode) ||S_ISFIFO(inode-i_mode) || S_ISSOCK(inode-i_mode)) {inode-i_op ext4_special_inode_operations;if (raw_inode-i_block[0])init_special_inode(inode, inode-i_mode,old_decode_dev(le32_to_cpu(raw_inode-i_block[0])));elseinit_special_inode(inode, inode-i_mode,new_decode_dev(le32_to_cpu(raw_inode-i_block[1])));} else if (ino EXT4_BOOT_LOADER_INO) {make_bad_inode(inode);} else {ret -EFSCORRUPTED;ext4_error_inode(inode, function, line, 0,iget: bogus i_mode (%o), inode-i_mode);goto bad_inode;}
}__ext4_iget函数首先调用iget_locked函数根据超级块和文件的inode号从inode高速缓存中获取struct inode对象如果从inode高速缓存中找到直接返回该struct inode对象如果没有找到分配一个struct inode对象然后调用__ext4_get_inode_loc函数和ext4_raw_inode函数根据ext4文件系统读取磁盘上的ext4 inode根据磁盘上的ext4 inode 来初始化struct inode对象然后将struct inode对象加入到inode高速缓存中以便下次inode加速查找。
3.2 LKM demo
接下来我们给出一个LKM示例根据inode号来获取对应的struct inode结构体
#include linux/init.h
#include linux/kernel.h
#include linux/module.h#include linux/fs.h
#include linux/fs_struct.h#include linux/kallsyms.h/* Module parameter */
static unsigned long ino 1837047;module_param(ino, ulong, 0);typedef enum {EXT4_IGET_NORMAL 0,EXT4_IGET_SPECIAL 0x0001, /* OK to iget a system inode */EXT4_IGET_HANDLE 0x0002 /* Inode # is from a handle */
} ext4_iget_flags;struct inode *(*my__ext4_iget)(struct super_block *sb, unsigned long ino,ext4_iget_flags flags, const char *function,unsigned int line);static int __init hello_init(void)
{struct fs_struct *fs;struct path pwd;//unsigned long ino 1837047;struct inode *inode;struct super_block *sb;fs current-fs;get_fs_pwd(fs, pwd);/* The root of the dentry tree */sb pwd.dentry-d_sb;my__ext4_iget (void *)kallsyms_lookup_name(__ext4_iget);inode my__ext4_iget(sb, ino, EXT4_IGET_NORMAL, __func__, __LINE__);if(inode)printk(inode num %ld\n, inode-i_ino);return -1;
}module_init(hello_init);MODULE_LICENSE(GPL);