Linux中mknod命令实现原理以及源码分析

Linux中mknod命令实现原理以及源码分析本篇文章以 mknod 创建字符设备文件进行讲解字符设备驱动的 Demo 例子可参考该篇文章 Linux 编写简单驱动并测试 1 mknod 命令 mknod dev helloc5200 该命令主要通过制定要创

大家好,欢迎来到IT知识分享网。

本篇文章以mknod创建字符设备文件进行讲解

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); } else shmem_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 */ else printk(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. 总结

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/127423.html

(0)
上一篇 2025-09-07 19:10
下一篇 2025-09-07 19:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信