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;elsereq.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_RESTOREint next_id;
#endifstruct rhashtable key_ht; // 哈希表
};

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

struct percpu_counter {raw_spinlock_t lock;s64 count;
#ifdef CONFIG_HOTPLUG_CPUstruct list_head list; /* 所有percpu_counters都在列表中 */
#endifs32 __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对象elsemnt = 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(); // 关联当前任务的credfc->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(); // 当前任务的uidctx->gid = current_fsgid(); // 当前任务的gidfc->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_SMPmnt->mnt_pcp = alloc_percpu(struct mnt_pcp); // 每cpu区域分配对象this_cpu_add(mnt->mnt_pcp->mnt_count, 1);  // mount计数赋值为1
#elsemnt->mnt_count = 1;mnt->mnt_writers = 0;
#endifINIT_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); // 分配一个未使用的IDif (res < 0)return res;mnt->mnt_id = res; // 关联idreturn 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队列最大值256ns->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分析>>

linux设备模型:devtmpfs虚拟文件系统分析相关推荐

  1. linux设备模型:pci驱动程序注册过程

    一个具有正常探测功能的pci驱动程序应具有基本的pci_driver结构实现,如: static struct pci_driver driver_ops = {.name = "drive ...

  2. linux设备文件的分类,Linux设备模型组件-类设备-设备类及subsystem

    Linux设备模型一.sysfs文件系统: sysfs文件系统是Linux2.6内核引入的,它被看成是与proc.devfs和devpty等同类别的文件系统,sysfs文件系统也是一个虚拟文件系统,它 ...

  3. linux设备驱动目录,Linux设备模型(5)_device和device driver

    Linux设备模型(5)_device和device driver 作者:wowo 发布于:2014-4-2 19:28 分类:统一设备模型 1. 前言 device和device driver是Li ...

  4. linux 统一设备模型 pci,linux设备模型____宏观印象

    linux设备模型____宏观印象 最近一个机会需要研究一个marvell芯片的设备的驱动,涉及驱动和一些用户态相关部分,正好学习一下驱动和sysfs,本文先是原理,后面的文章是详细描述.本文依托的是 ...

  5. Linux通常把设备对象抽象为,linux 设备模型(1)

    设备模型(一) 一.概述 从2.6内核引入了sysfs文件系统,与proc, devfs, devpty同类别,属于虚拟的文件系统.目的是展示设备驱动模型中各组件的层次关系,第一层目录:block, ...

  6. Linux设备模型组件-类设备-设备类及subsystem

    Linux设备模型   一.sysfs文件系统: sysfs文件系统是Linux2.6内核引入的,它被看成是与proc.devfs和devpty等同类别的文件系统,sysfs文件系统也是一个虚拟文件系 ...

  7. Linux 文件系统与设备文件系统 (二)—— sysfs 文件系统与Linux设备模型

    提到 sysfs 文件系统 ,必须先需要了解的是Linux设备模型,什么是Linux设备模型呢? 一.Linux 设备模型 1.设备模型概述 从2.6版本开始,Linux开发团队便为内核建立起一个统一 ...

  8. linux设备模型之kset/kobj/ktype分析

    1. 概述 今天来聊一下Linux设备模型的基石:kset/kobject/ktype. sysfs文件系统提供了一种用户与内核数据结构进行交互的方式,可以通过mount -t sysfs sysfs ...

  9. Linux设备驱动程序学习-Linux设备模型(总线、设备、驱动程序和类)

    文章的例子和实验使用<LDD3>所配的lddbus模块(稍作修改). 总线 总线是处理器和一个或多个设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟" ...

最新文章

  1. mysql filter_MySQL 过滤复制+复制映射 配置方法
  2. 你的对象在哪里?长什么样?我带你去看一看
  3. Linux学习(八)---crond 任务调度
  4. java hashmap 去重复_为什么我在Java HashMap中得到重复的键?
  5. 应用软件系统程序员的三个立面
  6. P3807-[模板]卢卡斯定理
  7. android显示网络图片控件,Android控件之ImageView(二)
  8. (筆記) 如何增加SignalTap II能觀察的reg與wire數量? (SOC) (Quartus II) (SignalTap II)
  9. SharePoint 已在此服务器场中安装 ID 为 XXXXXXXXX 的功能。请使用强制属性显式地重新安装此功能。解决方法...
  10. CAD绘制带角度的矩形阵列
  11. 中国数学发展历史回顾
  12. 7-22 循环日程表
  13. matlab仿真限幅发散,基于模糊控制的直流电机调速系统MATLAB仿真_贾东耀
  14. 计算机网络笔记(复习)
  15. 关于MySql的Unhandled异常
  16. 成本(CPU Costing)的含义
  17. 【2022/02/02】thinkphp源码详细阅读(一)
  18. 计算机浏览页面,计算机默认网页浏览器怎么设置
  19. 【PS必备】各系色卡对照表
  20. 基于Basys2开发板的简易电子琴和音乐播放器设计

热门文章

  1. 项目国际化I18N多语言切换
  2. 怎么样组建自己的团队
  3. 西瓜微数独家工具! 超全微博粉丝分析!!!
  4. 乘风破浪、厚积薄发国产服务器软件: LinWin Http Server
  5. 使用 C++ 编写万年历程序
  6. Leetcode hot100题 个人整理版
  7. mysql c盘满了 怎么办_C盘满了如何清理
  8. 分享调试SI4432的一些小经验
  9. 微信crm平台是什么系统?
  10. html标签em和i的区别,HTML中strong与b,em与i标签的区别和使用建议