• linux设备模型:devtmpfs虚拟文件系统分析


    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对象)等
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    internal_fs_type
    vfs_kern_mount

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    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); // 提交设备节点请求(增加或删除)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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, 
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      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,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      dev_fs_type 文件系统类型

    static struct file_system_type dev_fs_type = {
            .name = "devtmpfs",
            .mount = public_dev_mount,
    };
    
    • 1
    • 2
    • 3
    • 4

      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对象
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      set_root sysctl表根节(节点)对象

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

      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,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      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,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      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用户安装
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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(文件系统)私有数据释放
    };
    
    • 1
    • 2
    • 3
    • 4

      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,
    	},
    	{}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

      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,
            },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

      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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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(域名)名称
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      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];
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    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; // 哈希表
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      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,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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等
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    fs_context_for_mount

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

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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;
    		...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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, ¶m); // 向文件系统上下文提供单个挂载参数
    		// 在将参数传递到文件系统之前,首先检查该参数以查看它是否对应于标准挂载标志
    		//(在这种情况下,它用于设置SB_xxx标志并使用)或安全选项(在这种情况下,LSM 使用它)
    		
            kfree(param.string);     
            return ret;
    }
    EXPORT_SYMBOL(vfs_parse_fs_string);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

      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用户命名空间等等
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

      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结构对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
    ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

      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,                               
    		}    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

      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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

      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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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等命名空间
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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起可用),该标志具有相同的含义
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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命名空间操作和用户命名空间列表
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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(共享内存)参数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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命名空间分配标志...
    
    • 1
    • 2
    • 3

    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;
    ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

      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); // 分配文件系统上下文,并挂载
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

      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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

      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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    目录预览


    <>
    <>
    <>

  • 相关阅读:
    第二章《Java程序世界初探》第11节:switch结构
    Sprin、SpringBoot、微服务的概念
    Java类部类
    详解Docker的网络模式之host模式(host网络模式)
    在springboot中整合mybatis配置流程!
    大学生餐饮主题网页制作 美食网页设计模板 学生静态网页作业成品 dreamweaver美食甜品蛋糕HTML网站制作
    二轮平衡小车1:舵机与电机的基本控制
    前端页面城市显示级连
    Chainlit快速实现AI对话应用并将聊天数据的AWS S3 和 Azure Blob云服务中
    网站文章生成技术-网站文章生成工具免费
  • 原文地址:https://blog.csdn.net/a29562268/article/details/127719243