linux设备模型:devtmpfs虚拟文件系统分析
devtmpfs是一个设备文件系统,它将其所有文件保存在虚拟内存中。devtmpfs中的所有内容都是临时的,因为不会在您的硬盘驱动器上创建任何文件。如果卸载devtmpfs实例,其中存储的所有内容都将丢失。devtmpfs的根路径在/dev,它通过文件系统上下文创建mount(挂载)对象,使得用户层可以访问。devtmpfs通过devtmpfsd线程函数,分配新的命名空间代理(nsproxy)对象
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
初始化分配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;
}
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); // 提交设备节点请求(增加或删除)
}
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 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 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标识符结构
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等
if (name)
ret = vfs_parse_fs_string(fc, "source",
name, strlen(name)); // 向文件系统上下文提供单个挂载参数
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);
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, ¶m); // 向文件系统上下文提供单个挂载参数
// 在将参数传递到文件系统之前,首先检查该参数以查看它是否对应于标准挂载标志
//(在这种情况下,它用于设置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用户命名空间等等
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结构对象
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 分配新的命名空间代理(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起可用),该标志具有相同的含义
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命名空间分配标志...
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); // 分配文件系统上下文,并挂载
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 根据路径地址,解析名称
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 解析名称
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分析>>
更多推荐
所有评论(0)