Linux 内核中根据文件inode号获取其对应的struct inode

news2024/11/28 23:47:56

文章目录

  • 前言
  • 一、简介
  • 二、iget_locked
    • 2.1 简介
    • 2.2 内核中使用
    • 2.3 LKM demo
  • 三、ext4_iget
    • 3.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);
	else
		return 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_inode
	raw_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)、用户ID(i_uid)、组ID(i_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 ino=1837252
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.h

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;

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 函数,并在最后两个参数 funcLINE 中传递了调用该宏的函数名和行号。这些信息通常用于调试目的,以便在发生错误时能够追踪到具体的调用位置。

对于第三个参数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 inode
	inode = 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_inode
	ret = __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])));
		else
			init_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");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1176857.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

selenium自动化测试入门 —— 键盘鼠标事件ActionChains

在使用 Selenium WebDriver 做自动化测试的时候&#xff0c;会经常模拟鼠标和键盘的一些行为。比如使用鼠标单击、双击、右击、拖拽等动作&#xff1b;或者键盘输入、快捷键使用、组合键使用等模拟键盘的操作。在 WebDeriver 中&#xff0c;有一个专门的类来负责实现这些测试场…

【图像分类】【深度学习】【Pytorch版本】VggNet模型算法详解

【图像分类】【深度学习】【Pytorch版本】VggNet模型算法详解 文章目录 【图像分类】【深度学习】【Pytorch版本】VggNet模型算法详解前言VggNet讲解感受野感受野的计算公式3x3的卷积核的使用VggNet模型结构 VGGnet Pytorch代码完整代码总结 前言 Vgg(Visual Geometry Group)是…

爱思华宝邮件服务器本地文件包含漏洞

爱思华宝邮件服务器本地文件包含漏洞 免责声明漏洞描述漏洞影响漏洞危害网络测绘Fofa: title"Icewarp web klient" 漏洞复现1. 构造poc2. 读取文件 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地提高网络安全意识和技术水平。 任何人不…

mysql主从架构

mysql主从架构是一套非常基础的高可用架构&#xff0c;主要依赖复制技术来实现。 1.复制原理 mysql复制功能主要使用三个线程实现&#xff1a; 1.Binary log dump thread&#xff08;二进制日志转储线程&#xff09;:当副本连接时发送二进制日志 2.Replication I/O receiver …

glibc 里的线程 id

这里讲的是通过 pthread_create() 函数返回的线程 id&#xff0c;其实就是 glibc 库里维护的线程id&#xff0c;它跟内核维护的线程 id 不一样&#xff0c;pthread_create() 返回的线程 id 并不一定是唯一的。我们看 pthread_create 函数的源码&#xff0c;它最后返回的线程 id…

【数据分享】2000-2020年我国地级市旅游指标(6项指标/Excel格式/Shp格式)

旅游产业的发展情况是一个城市经济和文化影响力的重要指标&#xff0c;我们在很多研究中都会用到旅游相关的数据&#xff0c;之前我们也分享过一些相关数据&#xff0c;比如1978-2020年的中国旅游统计年鉴和2023年14847条的全国A级景区数据&#xff08;均可查看之前的文章获悉详…

Monarch Mixer:介绍一种性能比Transformer更强的网络架构

六年前&#xff0c;谷歌团队在arXiv上发表了革命性的论文《Attention is all you need》。作为一种优势的机器学习网络架构&#xff0c;Transformer技术迅速席卷全球。Transformer一直是现代基础模型背后的主力架构&#xff0c;并且在不同的应用程序中取得了令人印象深刻的成功…

vue2 集成 - 超图-SuperMap iClient3D for WebGL

1:下载SuperMap iClient3D for WebGL SuperMap iClient3D for WebGL产品包 打开资源目录如下 2:格式化项目中所用的依赖包 开发指南 从超图官网下载SuperMap iClient3D 11i (2023) SP1 for WebGL_CN.zip解压后,将Build目录下的SuperMap3D复制到项目中 \public\static…

Kmeans特征降维方法

文章目录 一、特征降维方法二、数据集介绍三、聚类问题及实现方法四、代码 一、特征降维方法 提取主要特征&#xff0c;忽略次要特征【PCA降维】合并相似特征【特征合并】 PCA主成分提取其实还是会忽略掉一些信息&#xff0c;有时候感觉聚类后的结果并不理想&#xff0c;所以…

非农数据不及预期,美元回落金价触及2000关口

上周五美国非农数据公布&#xff0c;现货黄金短线拉升近16美元&#xff0c;金价突破2000关口最高至2003.55美元/盎司&#xff0c;但随后金价转头回落&#xff0c;最终报收1992.19美元/盎司&#xff0c;涨幅收窄至0.34%。周线级别金价下跌0.61%&#xff0c;金价终止之前连续三周…

助力青少年学习,亚马逊云科技2024年全球人工智能和机器学习奖学金计划正式启动

云未来将以何种方式发展&#xff1f;方向握在意气风发的少年们手中。近日&#xff0c;亚马逊云科技全球人工智能和机器学习&#xff08;AI和ML&#xff09;奖学金计划在中国区的首次颁奖以及2024年启动仪式在北京中学正式举行&#xff0c;有45名学子凭借杰出的学业成绩、对人工…

【Linux】简单部署Yearning并结合内网穿透实现公网访问

文章目录 前言1. Linux 部署Yearning2. 本地访问Yearning3. Linux 安装cpolar4. 配置Yearning公网访问地址5. 公网远程访问Yearning管理界面6. 固定Yearning公网地址 前言 Yearning 简单&#xff0c;高效的MYSQL 审计平台 一款MYSQL SQL语句/查询审计工具&#xff0c;为DBA与开…

Centos7上Python克隆与对网页完整截图

有用的话谢谢点赞~ 安装Python3.11 cd /root wget https://www.python.org/ftp/python/3.11.0/Python-3.11.0.tgz tar -xzf Python-3.11.0.tgz yum -y install gcc zlib zlib-devel libffi libffi-devel yum install readline-devel yum install openssl-devel openssl11 ope…

TCP/IP协议群

TCP/IP协议群 什么是TCP/IP协议群 从字面意义上讲&#xff0c;有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议。实际生活当中有时也确实就是指这两种协议。然而在很多情况下&#xff0c;它只是利用 IP 进行通信时所必须用到的协议群的统称。具体来说&#xff0c;IP 或 ICMP、…

AI:62-基于深度学习的人体CT影像肺癌的识别与分类

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

MMdetection3.x个人笔记

1.在自定义数据集用训练出的权重进行可视化推理 input(jpg文件) model_config 这两个可以不用加前面的形参 然后用 \ 隔开 写上 --weight xx.pth python demo/image_demo.py data/coco_duck/train2017/10640.jpg work_dirs/solov2_r50_fpn_1x_coco/solov2_r50_fpn_1x_coco…

【MATLAB源码-第68期】基于matlab的802.11b 11Mbps CCK调制解调误码率仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 当涉及802.11b无线局域网&#xff08;Wi-Fi&#xff09;标准时&#xff0c;CCK&#xff08;Complementary Code Keying&#xff09;调制解调技术起着关键作用。下面解释一下CCK在802.11b中的工作原理和特点&#xff1a; 1. 数…

【QT】设置焦点及光标位置

很高兴在雪易的CSDN遇见你 ,给你糖糖 欢迎大家加入雪易社区-CSDN社区云 前言 本文分享Qt中如何设置焦点和光标位置的解决方案,并给出常见的问题解决方案,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(…

22款奔驰GLE450升级几何多光束大灯 车辆自检等功能

每个大灯配备的84个LED灯可确保提供最佳的道路照明&#xff0c;从而始终确保照明灯能够在需要时准确开启。利用位于风挡玻璃后方的摄像头提供的信息&#xff0c;4个控制单元能够以每秒100次的速度计算出理想的灯光模式 驾驶员能够全神贯注于交通状况&#xff0c;无需频繁切换近…