devtmpfs是一个设备文件系统,它将其所有文件保存在虚拟内存中。

devtmpfs中的所有内容都是临时的,因为不会在您的硬盘驱动器上创建任何文件。如果卸载devtmpfs实例,其中存储的所有内容都将丢失。

devtmpfs的根路径在/dev,它通过文件系统上下文创建mount(挂载)对象,使得用户层可以访问。

devtmpfs通过devtmpfsd线程函数,分配新的命名空间代理(nsproxy)对象,并分配、关联多类命名空间,如mnt、uts、ipc、pid、cgroup、net、time等,紧接着mount(挂载)文件系统,初始工作完成后,进入while循环函数(设备处理函数)。届时,devtmpfs进入工作状态,设备可以正常注册或移除。

设备通过device_add、device_del等相关的函数以设备节点形式(经过devtmpfs_submit_req函数)注册到requests对象,它属于req结构,通过handle函数删除或增加这个设备到(或删除)req的某一级next节点,期间会再次唤醒devtmpfsd线程函数,为设备节点近一步分配资源。

devtmpfs挂载属于初始化过程中的重要部分,通过fc_mount函数为文件系统上下文创建mount对象,分配mount结构对象并指定一个未使用的ID,设置mount设备名称,每cpu区域分配mount_pcp结构对象,初始化各种链接对象、关联init用户命名空间等等,关联根目录的用户命名空间,检查是否初始映射,返回vfsmount对象(用于快速访问超级块)等等。

init_mount函数还做了如下操作,解析名称(是否已经挂载),挂载路径过程包括安全块检查,使用超级用户权限,注册为内核模块,设置模块释放回调函数free_modprobe_argv,设置用户模式助手,初始化工作列表,队列执行函数call_usermodehelper_exec_work,启动一个用户模式的应用程序call_usermodehelper_exec_work,唤醒kmod_wq工作队列等等。

devtmpfs与设备模型的关联性不大,但它作为设备节点的挂载区间,还是把它列入设备模型系列比较合适一些。


目录


1. 函数分析

1.1 devtmpfs_init

1.2 devtmpfs_create_node

2. 源码结构

3. 部分结构定义

4. 扩展函数/变量

目录预览


1. 函数分析

1.1 devtmpfs_init

  初始化分配devtmpfs虚拟文件系统

  分配文件系统上下文,设置超级块标志、管理多类命令空间等,
  分配、关联文件共享内存选项对象,设置访问权限、当前的uid、gid,
  向文件系统上下文提供挂载参数,为文件系统上下文创建mount对象(返回vfsmount对象)等
  保存新的文件系统类型 到 文件系统类型列表,创建并唤醒线程,线程名称devtmpfsd

int __init devtmpfs_init(void)
{
        char opts[] = "mode=0755";
        int err;

        mnt = vfs_kern_mount(&internal_fs_type, 0, "devtmpfs", opts); // 分配文件系统上下文,设置超级块标志、管理多类命令空间等,分配、关联文件共享内存选项对象,设置访问权限、当前的uid、gid,向文件系统上下文提供挂载参数,为文件系统上下文创建mount对象(返回vfsmount对象)等

internal_fs_type
vfs_kern_mount

err = register_filesystem(&dev_fs_type); // 保存新的文件系统类型 到 文件系统类型列表

dev_fs_type
register_filesystem

		thread = kthread_run(devtmpfsd, &err, "kdevtmpfs"); // 创建并唤醒线程,线程名称devtmpfsd
		// devtmpfsd函数执行, 分配新的命名空间代理(nsproxy)对象,
		// 关联多类命名空间,如mnt、uts、ipc、pid、cgroup、net、time等命名空间,
		// 挂载文件系统,线程函数执行完成后, 进入while循环函数(设备处理函数),
		// 当接收到设备提交请求时(req结构),通过handle函数删除或增加这个设备到(或删除)req的某一级next节点,
		// 他相当于链表(从后向前添加)
		
        if (!IS_ERR(thread)) {
                wait_for_completion(&setup_done);  // 等待线程函数执行完成
                //  完成结构对象setup_done
        } else {
                err = PTR_ERR(thread);
                thread = NULL;
        }

	printk(KERN_INFO "devtmpfs: initialized\n");
	return 0;
}

devtmpfsd

1.2 devtmpfs_create_node

  devtmpfs虚拟文件系统提交设备节点请求(增加或删除)

int devtmpfs_create_node(struct device *dev)
{
        const char *tmp = NULL;
        struct req req;

        if (!thread)
                return 0;

        req.mode = 0;
        req.uid = GLOBAL_ROOT_UID;
        req.gid = GLOBAL_ROOT_GID;
        req.name = device_get_devnode(dev, &req.mode, &req.uid, &req.gid, &tmp); // 获取设备节点名称
		if (!req.name)
                return -ENOMEM;

        if (req.mode == 0)
                req.mode = 0600;
        if (is_blockdev(dev))
                req.mode |= S_IFBLK;
        else
                req.mode |= S_IFCHR;

        req.dev = dev;

        return devtmpfs_submit_req(&req, tmp); // 提交设备节点请求(增加或删除)
}

devtmpfs_submit_req


2. 源码结构

  internal_fs_type 文件系统描述

static struct file_system_type internal_fs_type = {
        .name = "devtmpfs", // 文件系统名称
#ifdef CONFIG_TMPFS
        .init_fs_context = shmem_init_fs_context, // 文件系统上下文,用于保存创建或重新配置超级块时使用的参数
#else
        .init_fs_context = ramfs_init_fs_context,
#endif
        .kill_sb = kill_litter_super, 
};

  legacy_init_fs_context 旧的文件系统上下文操作

const struct fs_context_operations legacy_fs_context_ops = {
        .free                   = legacy_fs_context_free,
        .dup                    = legacy_fs_context_dup,
        .parse_param            = legacy_parse_param,
        .parse_monolithic       = legacy_parse_monolithic,
        .get_tree               = legacy_get_tree,
        .reconfigure            = legacy_reconfigure,
};

  shmem_fs_context_ops 文件系统上下文操作

static const struct fs_context_operations shmem_fs_context_ops = {
	.free			= shmem_free_fc,
	.get_tree		= shmem_get_tree,
#ifdef CONFIG_TMPFS
	.parse_monolithic	= shmem_parse_options,
	.parse_param		= shmem_parse_one,
	.reconfigure		= shmem_reconfigure,
#endif
};

  dev_fs_type 文件系统类型

static struct file_system_type dev_fs_type = {
        .name = "devtmpfs",
        .mount = public_dev_mount,
};

  userns_operations 进程命名空间操作

onst struct proc_ns_operations userns_operations = {
        .name           = "user", // 空间名称
        .type           = CLONE_NEWUSER, // 用户命名空间类型
        .get            = userns_get,  // 从进程(任务)中获取用户命名空间
        .put            = userns_put, // 调度用户命名空间队列中的任务
        .install        = userns_install, // 装载用户命名空间
        .owner          = userns_owner, // 用户命名空间所属者(父级) 
        .get_parent     = ns_get_owner, // 从用户命名空间所属者中找到ns_common对象
};

  set_root sysctl表根节(节点)对象

static struct ctl_table_root set_root = {
        .lookup = set_lookup, // ctl_table_set对象
        .permissions = set_permissions, // 设置ipc权限
};

  utsns_operations uts命名空间操作

const struct proc_ns_operations utsns_operations = {
	.name		= "uts", // uts(UNIX分时系统)命名空间名称
	.type		= CLONE_NEWUTS, // 新建uts命名空间
	.get		= utsns_get, 
	.put		= utsns_put,
	.install	= utsns_install,
	.owner		= utsns_owner,
};

  ipcns_operations ipc命名空间操作

const struct proc_ns_operations ipcns_operations = {
        .name           = "ipc", 
        .type           = CLONE_NEWIPC, // 新建IPC空间
        .get            = ipcns_get,
        .put            = ipcns_put,
        .install        = ipcns_install,
        .owner          = ipcns_owner,
};

  mqueue_fs_type vfsmount队列操作

static struct file_system_type mqueue_fs_type = {
        .name                   = "mqueue", // vfsmount队列名称
        .init_fs_context        = mqueue_init_fs_context, // 初始化文件系统上下文,关联IPC命名空间的用户命名空间
        // 上下文私有数据为mqueue_fs_context结构,上下文操作结构为mqueue_fs_context_ops
        
        .kill_sb                = kill_litter_super, // 释放超级块
        .fs_flags               = FS_USERNS_MOUNT, // 可以由root用户安装
};

mqueue_fs_context_ops

  mqueue_fs_context_ops vfsmount上下文操作

static const struct fs_context_operations mqueue_fs_context_ops = {
        .free           = mqueue_fs_context_free, // vfsmount(文件系统)上下文释放
        .get_tree       = mqueue_get_tree,  // vfsmount(文件系统)私有数据释放
};

  mq_sysctls vfsmount sysctl表
  tbl表从此处拷贝

static struct ctl_table mq_sysctls[] = {
	{
		.procname	= "queues_max",
		.data		= &init_ipc_ns.mq_queues_max,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec,
	},
	{
		.procname	= "msg_max",
		.data		= &init_ipc_ns.mq_msg_max,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &msg_max_limit_min,
		.extra2		= &msg_max_limit_max,
	},
	{
		.procname	= "msgsize_max",
		.data		= &init_ipc_ns.mq_msgsize_max,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &msg_maxsize_limit_min,
		.extra2		= &msg_maxsize_limit_max,
	},
	{
		.procname	= "msg_default",
		.data		= &init_ipc_ns.mq_msg_default,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &msg_max_limit_min,
		.extra2		= &msg_max_limit_max,
	},
	{
		.procname	= "msgsize_default",
		.data		= &init_ipc_ns.mq_msgsize_default,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &msg_maxsize_limit_min,
		.extra2		= &msg_maxsize_limit_max,
	},
	{}
};

  sysctl_table_root sysctl表根节点

static struct ctl_table_root sysctl_table_root = {
        .default_set.dir.header = {
                {{.count = 1,
                  .nreg = 1,
                  .ctl_table = root_table }},
                .ctl_table_arg = root_table,
                .root = &sysctl_table_root,
                .set = &sysctl_table_root.default_set,
        },
};

3. 部分结构定义

  shmem_options 共享内存选项

struct shmem_options {
	unsigned long long blocks; // 块大小,以PAGE_SIZE块为单位
	unsigned long long inodes; // 最大索引节点数
	struct mempolicy *mpol; // 描述内存策略
	kuid_t uid;
	kgid_t gid;
	umode_t mode; // 模式
	bool full_inums;
	int huge; // 大页
	int seen;
#define SHMEM_SEEN_BLOCKS 1
#define SHMEM_SEEN_INODES 2
#define SHMEM_SEEN_HUGE 4
#define SHMEM_SEEN_INUMS 8
};

  uts_namespace UNIX Time-sharing System (UNIX分时系统)命名空间

  uts_namespace提供了两个系统标识符的隔离:主机名和NIS域名

struct uts_namespace {
	struct new_utsname name; // 内核名称、主机名称、内核版本号、域名等
	struct user_namespace *user_ns; // 用户命名空间
	struct ucounts *ucounts;
	struct ns_common ns;
} __randomize_layout;

new_utsname

  new_utsname uts名称结构的新版本

struct new_utsname {
	char sysname[__NEW_UTS_LEN + 1];  // 内核名称
	char nodename[__NEW_UTS_LEN + 1]; // 主机在网络节点上的名称或主机名称
	char release[__NEW_UTS_LEN + 1]; //  linux内核版本号
	char version[__NEW_UTS_LEN + 1]; // 操作系统版本号
	char machine[__NEW_UTS_LEN + 1]; // 主机的硬件(CPU架构)名称
	char domainname[__NEW_UTS_LEN + 1]; //  domain(域名)名称
};

  ucounts 用户命名空间列表

struct ucounts {
	struct hlist_node node; // 链表节点
	struct user_namespace *ns; // 用户命名空间
	kuid_t uid; // 用户标识(id)
	atomic_t count; // 引用计数
	atomic_long_t ucount[UCOUNT_COUNTS];  
	atomic_long_t rlimit[UCOUNT_RLIMIT_COUNTS];
};

  ipc_namespace ipc命名空间

struct ipc_namespace {
	struct ipc_ids	ids[3]; // IPC标识符结构

	int		sem_ctls[4];
	int		used_sems;

	unsigned int	msg_ctlmax;
	unsigned int	msg_ctlmnb;
	unsigned int	msg_ctlmni;
	struct percpu_counter percpu_msg_bytes;
	struct percpu_counter percpu_msg_hdrs;

	size_t		shm_ctlmax;
	size_t		shm_ctlall;
	unsigned long	shm_tot;
	int		shm_ctlmni;
	/*
	 * 定义是否强制对_all_ shm段执行IPC_RMID,而不管shmctl()
	 */
	int		shm_rmid_forced;

	struct notifier_block ipcns_nb;

	/* 关联vfsmount队列 */
	struct vfsmount	*mq_mnt;

	/* 此命名空间中的队列,受mq_lock保护 */
	unsigned int    mq_queues_count;

	/* next fields are set through sysctl */
	unsigned int    mq_queues_max;   /* initialized to DFLT_QUEUESMAX */
	unsigned int    mq_msg_max;      /* initialized to DFLT_MSGMAX */
	unsigned int    mq_msgsize_max;  /* initialized to DFLT_MSGSIZEMAX */
	unsigned int    mq_msg_default;
	unsigned int    mq_msgsize_default;

	struct ctl_table_set	mq_set;
	struct ctl_table_header	*mq_sysctls;

	struct ctl_table_set	ipc_set;
	struct ctl_table_header	*ipc_sysctls;

	/* user_ns which owns the ipc ns */
	struct user_namespace *user_ns;
	struct ucounts *ucounts;

	struct llist_node mnt_llist;

	struct ns_common ns;
} __randomize_layout;

ipc_ids

  ipc_ids IPC标识符结构

struct ipc_ids {
	int in_use; // 0表示未使用,非0(或1)表示正在使用中
	unsigned short seq; // 消息序列号
	struct rw_semaphore rwsem; // 读写信号量
	struct idr ipcs_idr;  // idr在linux内核中指的就是整数ID管理机制,这是将整数ID号和特定指针关联在一起的机制
	int max_idx; // 最大索引值
	int last_idx;	/* 用于环绕检测 */
#ifdef CONFIG_CHECKPOINT_RESTORE
	int next_id;
#endif
	struct rhashtable key_ht; // 哈希表
};

  percpu_counter CPUACCT控制器使用percpu_counter接口来收集用户和系统时间

struct percpu_counter {
	raw_spinlock_t lock;
	s64 count; 
#ifdef CONFIG_HOTPLUG_CPU
	struct list_head list;	/* 所有percpu_counters都在列表中 */
#endif
	s32 __percpu *counters;
};

  pidns_operations pid命名空间操作

const struct proc_ns_operations pidns_operations = {
        .name           = "pid",
        .type           = CLONE_NEWPID,
        .get            = pidns_get,
        .put            = pidns_put,
        .install        = pidns_install,
        .owner          = pidns_owner,
        .get_parent     = pidns_get_parent,
};

4. 扩展函数/变量

  vfs_kern_mount 分配文件系统上下文,设置超级块标志、管理多类命令空间等,分配、关联文件共享内存选项对象,设置访问权限、当前的uid、gid,向文件系统上下文提供挂载参数,为文件系统上下文创建mount对象(返回vfsmount对象)等

struct vfsmount *vfs_kern_mount(struct file_system_type *type,
                                int flags, const char *name,
                                void *data)
{
        struct fs_context *fc;
        struct vfsmount *mnt;
        int ret = 0;

		fc = fs_context_for_mount(type, flags); // 分配文件系统上下文,设置超级块标志、管理多类命令空间等,分配、关联文件共享内存选项对象,设置访问权限、当前的uid、gid等

fs_context_for_mount

		if (name)
                ret = vfs_parse_fs_string(fc, "source",
                                          name, strlen(name)); // 向文件系统上下文提供单个挂载参数

vfs_parse_fs_string

		if (!ret)       
                ret = parse_monolithic_mount_data(fc, data); // 解析挂载参数写入fc关联的fc_log结构对象中
                // 多次调用vfs_parse_fs_string函数

	 	if (!ret)
                mnt = fc_mount(fc); // 为文件系统上下文创建mount对象
                // 分配mount结构对象并指定一个未使用的ID,设置mount设备名称,
                // 每cpu区域分配mount_pcp结构对象,初始化各种链接对象、关联init用户命名空间等等,
                // 关联根目录的用户命名空间,检查是否初始映射,返回vfsmount对象
        else
                mnt = ERR_PTR(ret);

        put_fs_context(fc);
        return mnt;
}
EXPORT_SYMBOL_GPL(vfs_kern_mount);

fc_mount

  fs_context_for_mount 分配文件系统上下文,设置超级块标志、管理多类命令空间等,分配、关联文件共享内存选项对象,设置访问权限、当前的uid、gid等

struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
                                        unsigned int sb_flags)
{
        return alloc_fs_context(fs_type, NULL, sb_flags, 0,
                                        FS_CONTEXT_FOR_MOUNT);
}        
EXPORT_SYMBOL(fs_context_for_mount); 
||
\/
static struct fs_context *alloc_fs_context(struct file_system_type *fs_type,
                                      struct dentry *reference,
                                      unsigned int sb_flags,
                                      unsigned int sb_flags_mask,
                                      enum fs_context_purpose purpose)
{
        int (*init_fs_context)(struct fs_context *);
        struct fs_context *fc; // 文件系统上下文,用于保存创建或重新配置超级块时使用的参数
        int ret = -ENOMEM;

        fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL_ACCOUNT); 

		fc->purpose     = purpose; // 枚举类型
		// FS_CONTEXT_FOR_MOUNT  用于显式装载的新超级块
		// FS_CONTEXT_FOR_SUBMOUNT 用于自动子安装的新超级块 
		// FS_CONTEXT_FOR_RECONFIGURE 超级块重新配置(重新装载) 
		
        fc->sb_flags    = sb_flags; // 超级块标志
        fc->sb_flags_mask = sb_flags_mask; // 已更改的超级块标志
        fc->fs_type     = get_filesystem(fs_type); // 只有当我们拥有一个引用时,才能使用此选项
        fc->cred        = get_current_cred(); // 关联当前任务的cred
        fc->net_ns      = get_net(current->nsproxy->net_ns); // 关联当前任务中获取网络命名空间
        fc->log.prefix  = fs_type->name; // 文件系统名称

		mutex_init(&fc->uapi_mutex);

        switch (purpose) { 
        case FS_CONTEXT_FOR_MOUNT: // 用于显式装载的新超级块
                fc->user_ns = get_user_ns(fc->cred->user_ns); // 关联cred中的用户命名空间
                break;
        case FS_CONTEXT_FOR_SUBMOUNT: // 用于自动子安装的新超级块
                fc->user_ns = get_user_ns(reference->d_sb->s_user_ns); // 关联超级块根节点中的用户命名空间
                break;
        case FS_CONTEXT_FOR_RECONFIGURE: // 超级块重新配置(重新装载)
                atomic_inc(&reference->d_sb->s_active);
                fc->user_ns = get_user_ns(reference->d_sb->s_user_ns);  // 关联超级块根节点中的用户命名空间
                fc->root = dget(reference); // 关联根目录
                break;
        }

		 /* 使所有文件系统无条件支持此操作 */
        init_fs_context = fc->fs_type->init_fs_context; // 初始化文件系统上下文函数
        if (!init_fs_context)
                init_fs_context = legacy_init_fs_context; // 为不支持fs_context的文件系统初始化旧上下文

        ret = init_fs_context(fc); // 分配、关联文件共享内存选项对象,设置访问权限、当前的uid、gid等
        // shmem_init_fs_context

		...
		fc->need_free = true;
        return fc;
		...
}

legacy_init_fs_context
shmem_init_fs_context

  shmem_init_fs_context 分配、关联文件共享内存选项对象,设置访问权限、当前的uid、gid等

int shmem_init_fs_context(struct fs_context *fc)
{
	struct shmem_options *ctx;

	ctx = kzalloc(sizeof(struct shmem_options), GFP_KERNEL); // 分配共享内存选项对象

	ctx->mode = 0777 | S_ISVTX; // 权限
	ctx->uid = current_fsuid(); // 当前任务的uid
	ctx->gid = current_fsgid(); // 当前任务的gid

	fc->fs_private = ctx; // 关联共享内存选项对象
	fc->ops = &shmem_fs_context_ops; // 关联文件系统上下文操作对象
	return 0;
}

shmem_options
shmem_fs_context_ops

  vfs_parse_fs_string 向文件系统上下文提供单个挂载参数

int vfs_parse_fs_string(struct fs_context *fc, const char *key,
                        const char *value, size_t v_size)
{
        int ret;

        struct fs_parameter param = {
                .key    = key,  // 参数名称
                .type   = fs_value_is_flag, // 未给定值
                .size   = v_size,
        };

        if (value) {
                param.string = kmemdup_nul(value, v_size, GFP_KERNEL); //  分配内存及保存字符串
                // 增加KASAN(一种动态内存安全错误检测工具) 和 trace对kalloc的检查、调试信息
                
                param.type = fs_value_is_string;
        }

		ret = vfs_parse_fs_param(fc, &param); // 向文件系统上下文提供单个挂载参数
		// 在将参数传递到文件系统之前,首先检查该参数以查看它是否对应于标准挂载标志
		//(在这种情况下,它用于设置SB_xxx标志并使用)或安全选项(在这种情况下,LSM 使用它)
		
        kfree(param.string);     
        return ret;
}
EXPORT_SYMBOL(vfs_parse_fs_string);

  fc_mount 为文件系统上下文创建mount对象,分配mount结构对象并指定一个未使用的ID,设置mount设备名称,每cpu区域分配mount_pcp结构对象,初始化各种链接对象、关联init用户命名空间等等,关联根目录的用户命名空间,检查是否初始映射,返回vfsmount对象

struct vfsmount *fc_mount(struct fs_context *fc)
{
        int err = vfs_get_tree(fc); // 获取可装载根目录
		// 调用文件系统以获取或创建一个超级块,该超级块随后可用于挂载
		// 文件系统将一个指向根目录的指针放置在fc->root中,用于挂载

        if (!err) {
                up_write(&fc->root->d_sb->s_umount);
                return vfs_create_mount(fc);
        }
        return ERR_PTR(err);
}
EXPORT_SYMBOL(fc_mount);
||
\/
struct vfsmount *vfs_create_mount(struct fs_context *fc)
{       
        struct mount *mnt;
        struct user_namespace *fs_userns;

		mnt = alloc_vfsmnt(fc->source ?: "none"); // 分配mount结构对象并指定一个未使用的ID,设置mount设备名称,每cpu区域分配mount_pcp结构对象,初始化各种链接对象、关联init用户命名空间等等

alloc_vfsmnt

		if (fc->sb_flags & SB_KERNMOUNT) // kern_mount调用
                mnt->mnt.mnt_flags = MNT_INTERNAL; // 内部标志

		atomic_inc(&fc->root->d_sb->s_active);
        mnt->mnt.mnt_sb         = fc->root->d_sb; // 根超级块(根目录)
        mnt->mnt.mnt_root       = dget(fc->root); // 挂载的目录
		mnt->mnt_mountpoint     = mnt->mnt.mnt_root; 
        mnt->mnt_parent         = mnt;

		fs_userns = mnt->mnt.mnt_sb->s_user_ns;  // 关联根目录的用户命名空间
		// 用于解释文件系统uid、gid、配额、设备节点、xattr和安全标签的用户名称空间和默认上下文
		
        if (!initial_idmapping(fs_userns)) // 检查是否初始映射,如果是init命名空间是初始映射
                mnt->mnt.mnt_userns = get_user_ns(fs_userns);

        lock_mount_hash();
        list_add_tail(&mnt->mnt_instance, &mnt->mnt.mnt_sb->s_mounts); // 加入到s_mounts链表
        unlock_mount_hash();
        return &mnt->mnt; // 返回vfsmount对象
}
EXPORT_SYMBOL(vfs_create_mount);

  alloc_vfsmnt 分配mount结构对象并指定一个未使用的ID,设置mount设备名称,每cpu区域分配mount_pcp结构对象,初始化各种链接对象、关联init用户命名空间等等

static struct mount *alloc_vfsmnt(const char *name)
{
        struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); // 从mnt_cache缓存中分配内存
        if (mnt) {
                int err;

                err = mnt_alloc_id(mnt); // 分配一个未使用的ID,关联到mount结构对象

mnt_alloc_id

				if (name) {
                        mnt->mnt_devname = kstrdup_const(name,
                                                         GFP_KERNEL_ACCOUNT); // 复制现有常量字符串
                }

#ifdef CONFIG_SMP
                mnt->mnt_pcp = alloc_percpu(struct mnt_pcp); // 每cpu区域分配对象

                this_cpu_add(mnt->mnt_pcp->mnt_count, 1);  // mount计数赋值为1
#else
                mnt->mnt_count = 1;
                mnt->mnt_writers = 0;
#endif

				INIT_HLIST_NODE(&mnt->mnt_hash); // 初始化哈希列表
                INIT_LIST_HEAD(&mnt->mnt_child); 
                INIT_LIST_HEAD(&mnt->mnt_mounts);
                INIT_LIST_HEAD(&mnt->mnt_list);
                INIT_LIST_HEAD(&mnt->mnt_expire);
                INIT_LIST_HEAD(&mnt->mnt_share);
                INIT_LIST_HEAD(&mnt->mnt_slave_list);
                INIT_LIST_HEAD(&mnt->mnt_slave);
                INIT_HLIST_NODE(&mnt->mnt_mp_list);
                INIT_LIST_HEAD(&mnt->mnt_umounting);
                INIT_HLIST_HEAD(&mnt->mnt_stuck_children);
                mnt->mnt.mnt_userns = &init_user_ns; // 关联init用户命名空间
        }
        return mnt;
...
}

  mnt_alloc_id 分配一个未使用的ID,关联到mount结构对象

static int mnt_alloc_id(struct mount *mnt)
{
        int res = ida_alloc(&mnt_id_ida, GFP_KERNEL); // 分配一个未使用的ID

        if (res < 0)
                return res;
        mnt->mnt_id = res; // 关联id
        return 0;
}
||
\/
static DEFINE_IDA(mnt_id_ida);
||
\/
struct ida mnt_id_ida =  {  
        .xa =   {                            
	        .xa_lock = __SPIN_LOCK_UNLOCKED(mnt_id_ida.xa_lock),     
	        .xa_flags = IDA_INIT_FLAGS,                                   
	        .xa_head = NULL,                               
		}    
};

  register_filesystem 保存新的文件系统类型 到 文件系统类型列表

int register_filesystem(struct file_system_type * fs)
{
        int res = 0;
        struct file_system_type ** p;

        if (fs->parameters &&
            !fs_validate_description(fs->name, fs->parameters)) // 如果参数列表中有相同的参数名称,并且类型相同,返回错误
                return -EINVAL;

		BUG_ON(strchr(fs->name, '.')); // 文件系统上下文名称不能包含'.'
        if (fs->next)
                return -EBUSY;
        write_lock(&file_systems_lock);
        p = find_filesystem(fs->name, strlen(fs->name)); // 查找列表是否有重名的文件系统类型
        // static struct file_system_type *file_systems; 保存所有的文件系统类型
		if (*p)  // 如果文件系统已经存在(注册)
                res = -EBUSY;
        else
                *p = fs; // 保存到文件系统类型列表
        write_unlock(&file_systems_lock);
        return res;
}

  devtmpfsd 分配新的命名空间代理(nsproxy)对象,关联多类命名空间,如mnt、uts、ipc、pid、cgroup、net、time等命名空间,挂载文件系统,线程函数执行完成后, 进入while循环函数(设备处理函数),当接收到设备提交请求时(req结构),通过handle函数删除或增加这个设备到(或删除)req的某一级next节点,他相当于链表(从后向前添加)

static int __ref devtmpfsd(void *p)
{
        int err = devtmpfs_setup(p); // 分配新的命名空间代理(nsproxy)对象,关联多类命名空间,如mnt、uts、ipc、pid、cgroup、net、time等命名空间,挂载文件系统

        complete(&setup_done); // 函数已经处理完成
        
        if (err)
                return err;
        devtmpfs_work_loop(); //  进入while循环函数,当接收到设备请求时(req结构),通过handle函数删除或增加这个设备
        return 0;
}

devtmpfs_setup

  devtmpfs_setup 分配新的命名空间代理(nsproxy)对象,关联多类命名空间,如mnt、uts、ipc、pid、cgroup、net、time等命名空间,挂载文件系统

  取消大部分命名空间、共享虚拟机、共享信号队列等等

static noinline int __init devtmpfs_setup(void *p)
{
        int err;

        err = ksys_unshare(CLONE_NEWNS); // 进程取消共享“,并使用最初克隆共享的进程上下文的一部分
        // 如果取消共享用户名称空间,则必须同时取消共享线程组和文件系统根目录和工作目录
        // 如果取消共享虚拟机,也必须取消共享信号处理程序
        // 如果取消共享一个信号处理程序,也必须取消共享信号队列
        // 如果取消共享命名空间,也必须取消共享文件系统信息
        // CLONE_NEWIPC还必须从撤销列表中分离:在切换到新的ipc命名空间后,来自旧命名空间的信号量数组是不可访问的     
        // 如果正在共享文件系统结构,则取消共享,当前任务的fs->users不等于1的情况下,分配文件系统结构,并赋值部分参数,包括fs->users = 1,并替换文件系统结构到当前任务中
        // 如果正在共享文件描述符表,取消共享,并替换到当前任务中
        // 如果指定克隆用户命名空间标志,分配新的用户命名空间及新的凭据(red)
        // userns_operations 用户命名空间操作,set_root用户sysctl表
		// 取消共享nsproxy的所有命名空间部分,分配新的nsproxy对象
		// 分配一个命名空间代理(nsproxy)对象,关联多类命名空间,如mnt、uts、ipc、pid、cgroup、net、time等命名空间

userns_operations
set_root
create_new_namespaces
perf_event_namespaces

		err = init_mount("devtmpfs", "/", "devtmpfs", DEVTMPFS_MFLAGS, NULL);  // 挂载文件系统
		// 根据路径地址,解析名称(解析成功继续执行),挂载路径过程包括安全块检查,
		// 使用的超级用户权限,注册为模块,设置模块释放回调函数free_modprobe_argv,
		// 设置用户模式助手,初始化工作列表,队列执行函数call_usermodehelper_exec_work
		// 启动一个用户模式的应用程序call_usermodehelper_exec_work
		// 唤醒kmod_wq工作队列,创建文件系统上下文
		// #define DEVTMPFS_MFLAGS       (MS_SILENT)
		// MS_SILENT(自Linux 2.6.17起) 禁止在内核日志中显示某些printk() 警告消息
		// 该标志取代了名称错误且过时的MS_VERBOSE标志(自Linux 2.4.12起可用),该标志具有相同的含义

init_mount

  create_new_namespaces 分配一个命名空间代理(nsproxy)对象,关联多类命名空间,如mnt、uts、ipc、pid、cgroup、net、time等命名空间

static struct nsproxy *create_new_namespaces(unsigned long flags,
	struct task_struct *tsk, struct user_namespace *user_ns,
	struct fs_struct *new_fs)
{
	struct nsproxy *new_nsp; 命名空间代理结构
	int err;

	new_nsp = create_nsproxy(); // 分配nsproxy对象
	// 包含指向所有进程名称空间的指针的结构——fs (mount)、uts、network、sysvipc等

	new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, user_ns, new_fs); // 分配mnt命名空间对象,并设置文件系统的根挂载目录和当前挂载目录

	new_nsp->uts_ns = copy_utsname(flags, user_ns, tsk->nsproxy->uts_ns); // 分配uts命名空间对象,关联用户命名空间、uts命名空间操作和用户命名空间列表

uts_namespace
utsns_operations
ucounts

	new_nsp->ipc_ns = copy_ipcs(flags, user_ns, tsk->nsproxy->ipc_ns); // 如果设置CLONE_NEWIPC标志,分配ipc命名空间对象
	// 关联ipc命名空间操作和用户命名空间列表, vfsmount队列初始化ipc命名空间
	// 设置sysctl表,mq_sysctls中拷贝到tbl表,注册sysctl表 (/proc/sys/fs/mqueue),并对tbl表分层(条目)
	// 初始化ctl_table_header对象关联tbl表、根目录、sysctl注册表(ctl_table_set), 并关联到ctl_node对象中
	// 检查ctl_node中的所有节点,从根节点到子节进行红黑树平衡节点,并进行复色
	// ipc命名空间关联percpu_counter对象(在每cpu内存中分配percpu_msg_bytes和percpu_msg_hdrs变量,关联用户命名空间列表)
	// 初始化ipc命名空间关联的ids(IPC标识符结构数组),初始化sem(信号量)参数和shm(共享内存)参数

ipcns_operations
mq_init_ns
mq_sysctls
sysctl_table_root
percpu_counter

new_nsp->pid_ns_for_children =
                copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns_for_children); // 如果设置CLONE_NEWPID标志,分配pid命名空间对象
                // 关联分配pid缓存,关联pid命名空间操作,设置pid命名空间分配标志...

pidns_operations

new_nsp->cgroup_ns = copy_cgroup_ns(flags, user_ns,
                                            tsk->nsproxy->cgroup_ns); // 分配cgroup命名空间

new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns); // 分配net命名空间

new_nsp->time_ns_for_children = copy_time_ns(flags, user_ns,
                                        tsk->nsproxy->time_ns_for_children); // 分配time命名空间

new_nsp->time_ns = get_time_ns(tsk->nsproxy->time_ns); // 关联time命名空间

return new_nsp;
...
}

  mq_init_ns vfsmount队列初始化ipc命名空间

  分配文件系统上下文(vfsmount队列操作),关联ipc命名空间,关联用户命名空间,
  为文件系统上下文创建mount对象,分配mount结构对象并指定一个未使用的ID,
  设置mount设备名称,每cpu区域分配mount_pcp结构对象,初始化各种链接对象、
  关联init用户命名空间等等,关联根目录的用户命名空间,检查是否初始映射,
  s_mounts关联到mount实例链表,返回vfsmount对象,最后ipc命名空间关联vfsmount对象

int mq_init_ns(struct ipc_namespace *ns)
{
        struct vfsmount *m;

        ns->mq_queues_count  = 0;  // vfsmount队列计数
        ns->mq_queues_max    = DFLT_QUEUESMAX; // vfsmount队列最大值256
        ns->mq_msg_max       = DFLT_MSGMAX; // 队列消息最多10条
        ns->mq_msgsize_max   = DFLT_MSGSIZEMAX; // 每条消息最大8192字节
        ns->mq_msg_default   = DFLT_MSG; // 默认10条
        ns->mq_msgsize_default  = DFLT_MSGSIZE; // 默认8192字节

        m = mq_create_mount(ns); // 分配文件系统上下文,... 返回vfsmount对象
        if (IS_ERR(m))
                return PTR_ERR(m);
        ns->mq_mnt = m; // ipc命名空间关联vfsmount对象
        return 0;
}
||
\/
static struct vfsmount *mq_create_mount(struct ipc_namespace *ns)
{
        struct mqueue_fs_context *ctx;
        struct fs_context *fc;
        struct vfsmount *mnt;

        fc = fs_context_for_mount(&mqueue_fs_type, SB_KERNMOUNT); // 分配文件系统上下文,并挂载

ipc_namespace
mqueue_fs_type

		ctx = fc->fs_private; // mqueue_init_fs_context函数中分配的私有数据字段 mqueue_fs_context结构
        ctx->newns = true;
        put_ipc_ns(ctx->ipc_ns); 
        ctx->ipc_ns = get_ipc_ns(ns); // 关联ipc命名空间
        put_user_ns(fc->user_ns);
        fc->user_ns = get_user_ns(ctx->ipc_ns->user_ns); // 关联用户命名空间

        mnt = fc_mount(fc); // 为文件系统上下文创建mount对象
        // 分配mount结构对象并指定一个未使用的ID,设置mount设备名称,
        // 每cpu区域分配mount_pcp结构对象,
        // 初始化各种链接对象、关联init用户命名空间等等,
        // 关联根目录的用户命名空间,检查是否初始映射,s_mounts关联到mount实例链表,返回vfsmount对象
        
        put_fs_context(fc);
        return mnt;
}

  init_mount 根据路径地址,解析名称(解析成功继续执行),挂载路径过程包括安全块检查,
  使用的超级用户权限,注册为模块,设置模块释放回调函数free_modprobe_argv,
  设置用户模式助手,初始化工作列表,队列执行函数call_usermodehelper_exec_work
  启动一个用户模式的应用程序call_usermodehelper_exec_work
  唤醒kmod_wq工作队列,创建文件系统上下文

int __init init_mount(const char *dev_name, const char *dir_name,
                const char *type_page, unsigned long flags, void *data_page)
{
        struct path path;
        int ret;

        ret = kern_path(dir_name, LOOKUP_FOLLOW, &path); // 根据路径地址,解析名称
        // #define LOOKUP_FOLLOW           0x0001  /* 尾随链接 */

		ret = path_mount(dev_name, &path, type_page, flags, data_page); // 路径挂载
		// 安全块检查,使用的超级用户权限
		// 注册为模块,设置模块释放回调函数free_modprobe_argv
		// 设置用户模式助手,初始化工作列表,队列执行函数call_usermodehelper_exec_work
		// 启动一个用户模式的应用程序call_usermodehelper_exec_work
		// 唤醒kmod_wq工作队列,创建文件系统上下文
	path_put(&path);
	return ret;
}

kern_path

  kern_path 根据路径地址,解析名称

int kern_path(const char *name, unsigned int flags, struct path *path)
{
        struct filename *filename = getname_kernel(name); // 分配filename结构对象存储路径名称
        // 分配audit名称,并关联到aname成员
        
        int ret = filename_lookup(AT_FDCWD, filename, flags, path, NULL); // 根据路径地址,解析名称
        // 当前进程(current)关联nameidata对象
        // nameidata对象关联根节点、名称等等

        putname(filename);
        return ret;

}
EXPORT_SYMBOL(kern_path);

path_lookupat

  path_lookupat 解析名称

static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path)
{
        const char *s = path_init(nd, flags);
        int err;

		...
		while (!(err = link_path_walk(s, nd)) &&
               (s = lookup_last(nd)) != NULL) // 名称解析存在 
                ;

		...
		if (!err)
			err = complete_walk(nd);

		...
		terminate_walk(nd);
	return err;
}

  devtmpfs_submit_req 提交设备节点请求

static int devtmpfs_submit_req(struct req *req, const char *tmp)
{
        init_completion(&req->done); // 初始化完成结构

        spin_lock(&req_lock);
        req->next = requests; // 上一个设备节点
        requests = req; // 当前req设备请求结构放在上一个设备之前
        spin_unlock(&req_lock);

        wake_up_process(thread); // 唤醒devtmpfsd线程
        wait_for_completion(&req->done); // 等待线程执行完成

        kfree(tmp);

        return req->err;
}

目录预览


<<linux设备模型:sysfs(kobject)解析>>
<<linux设备模型:kset及设备驱动抽象类(class)分析>>
<<linux设备模型:bus概念及pci_bus分析>>

Logo

更多推荐