Linux中mknod命令实现原理以及源码分析
本篇文章以mknod创建字符设备文件进行讲解
字符设备驱动的Demo例子可参考该篇文章
Linux 编写简单驱动并测试
1. mknod 命令
mknod /dev/hello c 520 0
该命令主要通过制定要创建的设备文件名称/dev/hello
,以及设备类型c
字符设备,最后的520 0
表示为主设备号和次设备号。
当我们使用该命令创建好了/dev/hello
设备文件,当我们在用户态对该文件进行open()
、write()
、read()
时,就会调用到/dev/hello
设备文件对应的设备驱动的file_operations
对应的.open
、.write
、.read
回调函数。
那么这一切是怎么做到的呢?这就跟我们的mknod
命令的实现有关。
2. mknod 原理
当我们输入mknod
命令时,实际上会创建设备文件/dev/hello
和所对应的inode
,以及将主设备号和次设备号形成的设备号保存在inode
的i_rdev
中。
3. mknod 源码分析
已经知道mknode
主要是为设备文件创建inode
的原理后,我们来看一下代码是如何实现的。
3.1 mknod
SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev)
{return do_mknodat(AT_FDCWD, getname(filename), mode, dev);
}
这个是mknod
命令的系统调用,我们键入的命令mknod /dev/hello c 520 0
,将会为这个函数进行赋值,赋值的内容如下:
filename = "/dev/hello";
mode = mode | S_IFCHR;
dev = MKDEV(520, 0);
3.2 do_mknodat
static int do_mknodat(int dfd, struct filename *name, umode_t mode,unsigned int dev)
{struct user_namespace *mnt_userns;struct dentry *dentry;struct path path;int error;unsigned int lookup_flags = 0;error = may_mknod(mode);if (error)goto out1;
retry:dentry = filename_create(dfd, name, &path, lookup_flags);error = PTR_ERR(dentry);if (IS_ERR(dentry))goto out1;if (!IS_POSIXACL(path.dentry->d_inode))mode &= ~current_umask();error = security_path_mknod(&path, dentry, mode, dev);if (error)goto out2;mnt_userns = mnt_user_ns(path.mnt);switch (mode & S_IFMT) {case 0: case S_IFREG:error = vfs_create(mnt_userns, path.dentry->d_inode,dentry, mode, true);if (!error)ima_post_path_mknod(mnt_userns, dentry);break;case S_IFCHR: case S_IFBLK:error = vfs_mknod(mnt_userns, path.dentry->d_inode,dentry, mode, new_decode_dev(dev));break;case S_IFIFO: case S_IFSOCK:error = vfs_mknod(mnt_userns, path.dentry->d_inode,dentry, mode, 0);break;}
out2:done_path_create(&path, dentry);if (retry_estale(error, lookup_flags)) {lookup_flags |= LOOKUP_REVAL;goto retry;}
out1:putname(name);return error;
}
由mknod()
函数调用到do_mknodat()
函数进行下一步的处理,在这个函数中我们可以看到几个主要流程步骤:
filename_create()
函数初始化了一个struct dentry
还有一个struct path
,其中dentry
表示当前/dev/hello
的信息,而path
则表示/dev
的路径信息。vfs_mknod()
该函数是最主要的处理部分。由于我们输入的mknod /dev/hello c 520 0
命令会让mode & S_IFCHR
情况成立,所以会进入到vfs_mknod()
创建inode
流程。
3.3 vfs_mknod
int vfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,struct dentry *dentry, umode_t mode, dev_t dev)
{bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;int error = may_create(mnt_userns, dir, dentry);if (error)return error;if ((S_ISCHR(mode) || S_ISBLK(mode)) && !is_whiteout &&!capable(CAP_MKNOD))return -EPERM;if (!dir->i_op->mknod)return -EPERM;error = devcgroup_inode_mknod(mode, dev);if (error)return error;error = security_inode_mknod(dir, dentry, mode, dev);if (error)return error;error = dir->i_op->mknod(mnt_userns, dir, dentry, mode, dev);if (!error)fsnotify_create(dir, dentry);return error;
}
EXPORT_SYMBOL(vfs_mknod);
vfs_mknod()
函数是创建inode
的公共流程函数,现在可以分析一下传入的主要参数信息:
struct inode *dir
:/dev
目录所指向的inode
信息struct dentry *dentry
:/dev/hello
我们要创建的设备文件的dentry
umode_t mode
:该mode
是指的我们要创建的设备文件类型,如我们键入的mknod /dev/hello c 520 0
命令,所以我们要创建的是字符设备,也就是说S_ISCHR(mode)等于truedev_t dev
:和开始进入mknod系统调用一样都是,dev = MKDEV(520, 0)
从vfs_mknod()
函数来看,最终会由/dev
所指向的inode
中的i_op
中的mknod
回调函数进行处理,因此到这里算是分析一部分了。
而我们的/dev
所指向的inode
中的i_op
中的mknod
回调函数到底是什么呢?
答案:shmem_mknod()
函数,具体为什么?是因为/dev
是一个目录,这个目录对应的文件系统是devtmpfs
,而这个devtmpfs
文件系统所使用的struct inode_operations
操作是shmem_dir_inode_operations
,因此会调用到shmem_mknod()
函数。
3.4 shmem_mknod
static int
shmem_mknod(struct user_namespace *mnt_userns, struct inode *dir,struct dentry *dentry, umode_t mode, dev_t dev)
{struct inode *inode;int error = -ENOSPC;inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);if (inode) {error = simple_acl_create(dir, inode);if (error)goto out_iput;error = security_inode_init_security(inode, dir,&dentry->d_name,shmem_initxattrs, NULL);if (error && error != -EOPNOTSUPP)goto out_iput;error = 0;dir->i_size += BOGO_DIRENT_SIZE;dir->i_ctime = dir->i_mtime = current_time(dir);d_instantiate(dentry, inode);dget(dentry); /* Extra count - pin the dentry in core */}return error;
out_iput:iput(inode);return error;
}
由shmem_mknod()
调用到shmem_get_inode()
函数完成最终的/dev/hello
设备文件所对应的inode
的创建。
3.4 shmem_get_inode
static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir,umode_t mode, dev_t dev, unsigned long flags)
{struct inode *inode;struct shmem_inode_info *info;struct shmem_sb_info *sbinfo = SHMEM_SB(sb);ino_t ino;if (shmem_reserve_inode(sb, &ino))return NULL;inode = new_inode(sb);if (inode) {inode->i_ino = ino;inode_init_owner(&init_user_ns, inode, dir, mode);inode->i_blocks = 0;inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);inode->i_generation = prandom_u32();info = SHMEM_I(inode);memset(info, 0, (char *)inode - (char *)info);spin_lock_init(&info->lock);atomic_set(&info->stop_eviction, 0);info->seals = F_SEAL_SEAL;info->flags = flags & VM_NORESERVE;info->i_crtime = inode->i_mtime;INIT_LIST_HEAD(&info->shrinklist);INIT_LIST_HEAD(&info->swaplist);simple_xattrs_init(&info->xattrs);cache_no_acl(inode);mapping_set_large_folios(inode->i_mapping);switch (mode & S_IFMT) {default:inode->i_op = &shmem_special_inode_operations;init_special_inode(inode, mode, dev);break;case S_IFREG:inode->i_mapping->a_ops = &shmem_aops;inode->i_op = &shmem_inode_operations;inode->i_fop = &shmem_file_operations;mpol_shared_policy_init(&info->policy,shmem_get_sbmpol(sbinfo));break;case S_IFDIR:inc_nlink(inode);/* Some things misbehave if size == 0 on a directory */inode->i_size = 2 * BOGO_DIRENT_SIZE;inode->i_op = &shmem_dir_inode_operations;inode->i_fop = &simple_dir_operations;break;case S_IFLNK:/** Must not load anything in the rbtree,* mpol_free_shared_policy will not be called.*/mpol_shared_policy_init(&info->policy, NULL);break;}lockdep_annotate_inode_mutex_key(inode);} elseshmem_free_inode(sb);return inode;
}
从shmem_get_inode()
函数可以得到几个关键步骤:
inode = new_inode(sb);
这里会分配一个inode
- 如果分配到
inode
,则会对inode
进行一系列的初始化设置 - 最后由
init_special_inode
完成对inode
最后的设置
3.5 init_special_inode
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{inode->i_mode = mode;if (S_ISCHR(mode)) {inode->i_fop = &def_chr_fops;inode->i_rdev = rdev;} else if (S_ISBLK(mode)) {inode->i_fop = &def_blk_fops;inode->i_rdev = rdev;} else if (S_ISFIFO(mode))inode->i_fop = &pipefifo_fops;else if (S_ISSOCK(mode)); /* leave it no_open_fops */elseprintk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"" inode %s:%lu\n", mode, inode->i_sb->s_id,inode->i_ino);
}
EXPORT_SYMBOL(init_special_inode);
从init_special_inode()
函数可以看出来,当执行mknod /dev/hello c 520 0
命令为/dev/hello
设备文件生成的inode
时,由于指定的设备文件类型为字符类型,所以会为inode
进行如下赋值操作:
if (S_ISCHR(mode)) {inode->i_fop = &def_chr_fops;inode->i_rdev = rdev;
}
4. 总结
到这里对于Linux中mknod命令实现原理以及源码分析已经结束。
我们执行mknod命令最终只是生成一个/dev/hello
设备文件,而且为该设备文件生成对应的inode
信息而已。
至于这个inode
的信息如何使用,请看下一章节:
Linux中open命令实现原理以及源码分析
谢谢大家浏览,本博客为博主原创,未经允许不可转载。
Linux中mknod命令实现原理以及源码分析相关推荐
- 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]
目录 前言 现象 源码分析 HandlerMethodArgumentResolver与HandlerMethodReturnValueHandler接口介绍 HandlerMethodArgumen ...
- 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析] good
目录 前言 现象 源码分析 HandlerMethodArgumentResolver与HandlerMethodReturnValueHandler接口介绍 HandlerMethodArgumen ...
- Java并发包中Semaphore的工作原理、源码分析及使用示例
简介: 在多线程程序设计中有三个同步工具需要我们掌握,分别是Semaphore(信号量),countDownLatch(倒计数门闸锁),CyclicBarrier(可重用栅栏) 欢迎探讨,如有错误敬请 ...
- ConcurrentHashMap实现原理及源码分析
ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...
- concurrenthashmap_ConcurrentHashMap实现原理及源码分析
ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...
- SIFT原理与源码分析:DoG尺度空间构造
<SIFT原理与源码分析>系列文章索引:http://blog.csdn.net/xiaowei_cqu/article/details/8069548 尺度空间理论 自然界中的物体随着观 ...
- 深入理解Spark 2.1 Core (十二):TimSort 的原理与源码分析
在博文<深入理解Spark 2.1 Core (十):Shuffle Map 端的原理与源码分析 >中我们提到了: 使用Sort等对数据进行排序,其中用到了TimSort 这篇博文我们就来 ...
- 深入理解Spark 2.1 Core (十一):Shuffle Reduce 端的原理与源码分析
我们曾经在<深入理解Spark 2.1 Core (一):RDD的原理与源码分析 >讲解过: 为了有效地实现容错,RDD提供了一种高度受限的共享内存,即RDD是只读的,并且只能通过其他RD ...
- 深入理解Spark 2.1 Core (十):Shuffle Map 端的原理与源码分析
在上一篇<深入理解Spark 2.1 Core (九):迭代计算和Shuffle的原理与源码分析>提到经过迭代计算后, SortShuffleWriter.write中: // 根据排序方 ...
最新文章
- 在Redis中设置了过期时间的Key,需要注意哪些问题?
- 递归查询树状结构某个确定的节点
- boost::math模块二项式分布来预测概率 抛硬币时的正面和反面的测试程序
- 计算机控制系统的稳态误差,计算机控制系统的稳态误差
- Operating System-Thread(5)弹出式线程使单线程代码多线程化会产生那些有关问题
- web developer tips (1):创建、管理、应用样式表的强大工具
- 牵手大企,关于图形计算、HPC与AI,NVIDIA言有尽而意无穷!
- 安装MySQL数据库无法启动服务的完美解决办法
- 若依单体版本代码生成模块使用教程
- 使用Animation编辑器编辑动画
- 上大计算机专业在哪校区,好消息!成都理工大学在川扩招751人,宜宾校区新增计算机443人...
- JavaScript经纬度和地图坐标相互转换
- 带农历的html日历插件,魔镜日历插件-可生成带农历的日历月历年历-CDR插件
- 吕布机器人评测_999元的吕布机器人到底怎么样?听听玩家们怎么说
- 邓应海:下周黄金走势分析,警惕直接跳高
- 读书笔记--推荐系统实践(4)
- 全自动加药装置自动加药系统的技术说明
- oracle 官网邮箱和密码是什么,我是如何进入21cn内网的(可能获取用户邮箱密码)...
- 网络变压器的介绍分类及工作原理
- 将网页制作成chm文件