本篇文章以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,以及将主设备号和次设备号形成的设备号保存在inodei_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()函数进行下一步的处理,在这个函数中我们可以看到几个主要流程步骤:

  1. filename_create()函数初始化了一个struct dentry还有一个struct path,其中dentry表示当前/dev/hello的信息,而path则表示/dev的路径信息。
  2. 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)等于true
  • dev_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()函数可以得到几个关键步骤:

  1. inode = new_inode(sb);这里会分配一个inode
  2. 如果分配到inode,则会对inode进行一系列的初始化设置
  3. 最后由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命令实现原理以及源码分析相关推荐

  1. 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]

    目录 前言 现象 源码分析 HandlerMethodArgumentResolver与HandlerMethodReturnValueHandler接口介绍 HandlerMethodArgumen ...

  2. 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析] good

    目录 前言 现象 源码分析 HandlerMethodArgumentResolver与HandlerMethodReturnValueHandler接口介绍 HandlerMethodArgumen ...

  3. Java并发包中Semaphore的工作原理、源码分析及使用示例

    简介: 在多线程程序设计中有三个同步工具需要我们掌握,分别是Semaphore(信号量),countDownLatch(倒计数门闸锁),CyclicBarrier(可重用栅栏) 欢迎探讨,如有错误敬请 ...

  4. ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...

  5. concurrenthashmap_ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...

  6. SIFT原理与源码分析:DoG尺度空间构造

    <SIFT原理与源码分析>系列文章索引:http://blog.csdn.net/xiaowei_cqu/article/details/8069548 尺度空间理论 自然界中的物体随着观 ...

  7. 深入理解Spark 2.1 Core (十二):TimSort 的原理与源码分析

    在博文<深入理解Spark 2.1 Core (十):Shuffle Map 端的原理与源码分析 >中我们提到了: 使用Sort等对数据进行排序,其中用到了TimSort 这篇博文我们就来 ...

  8. 深入理解Spark 2.1 Core (十一):Shuffle Reduce 端的原理与源码分析

    我们曾经在<深入理解Spark 2.1 Core (一):RDD的原理与源码分析 >讲解过: 为了有效地实现容错,RDD提供了一种高度受限的共享内存,即RDD是只读的,并且只能通过其他RD ...

  9. 深入理解Spark 2.1 Core (十):Shuffle Map 端的原理与源码分析

    在上一篇<深入理解Spark 2.1 Core (九):迭代计算和Shuffle的原理与源码分析>提到经过迭代计算后, SortShuffleWriter.write中: // 根据排序方 ...

最新文章

  1. 在Redis中设置了过期时间的Key,需要注意哪些问题?
  2. 递归查询树状结构某个确定的节点
  3. boost::math模块二项式分布来预测概率 抛硬币时的正面和反面的测试程序
  4. 计算机控制系统的稳态误差,计算机控制系统的稳态误差
  5. Operating System-Thread(5)弹出式线程使单线程代码多线程化会产生那些有关问题
  6. web developer tips (1):创建、管理、应用样式表的强大工具
  7. 牵手大企,关于图形计算、HPC与AI,NVIDIA言有尽而意无穷!
  8. 安装MySQL数据库无法启动服务的完美解决办法
  9. 若依单体版本代码生成模块使用教程
  10. 使用Animation编辑器编辑动画
  11. 上大计算机专业在哪校区,好消息!成都理工大学在川扩招751人,宜宾校区新增计算机443人...
  12. JavaScript经纬度和地图坐标相互转换
  13. 带农历的html日历插件,魔镜日历插件-可生成带农历的日历月历年历-CDR插件
  14. 吕布机器人评测_999元的吕布机器人到底怎么样?听听玩家们怎么说
  15. 邓应海:下周黄金走势分析,警惕直接跳高
  16. 读书笔记--推荐系统实践(4)
  17. 全自动加药装置自动加药系统的技术说明
  18. oracle 官网邮箱和密码是什么,我是如何进入21cn内网的(可能获取用户邮箱密码)...
  19. 网络变压器的介绍分类及工作原理
  20. 将网页制作成chm文件

热门文章

  1. 4K激光电视进入万元时代,极米引领新一轮客厅革命
  2. 第七次人口普查数据与城镇化
  3. 产品经理书籍推荐(二)
  4. 计算机系统硬盘拷贝,不用互助,轻松搞定新旧电脑间系统完整复制或硬盘更换!...
  5. 【ES】windows启动
  6. CSDN 博客备份工具
  7. 树莓派3b+指南(十八)高帧率opencv卡顿问题解决方案
  8. 开发互动直播应用很简单:声网 Android Demo保姆级运行教程
  9. CRO的作用,现状,发展和名录
  10. Selector多路复用器原理