分享

devtmpfs文件系统创建设备节点

 clover_xian 2014-01-03
  • 一、devtmpfs概述
  • 1.devtmpfs 的功用是在 Linux 核心 启动早期建立一个初步的 /dev,令一般启动程序不用等待 udev,缩短 GNU/Linux 的开机时间。

  • 2.重要解释
  • Devtmpfs lets the kernel create a tmpfs very early at kernel initialization, before any driver core device is registered. Every device with a major/minor will have a device node created in this tmpfs instance. After the rootfs is mounted by the kernel, the populated tmpfs is mounted at /dev. In initramfs, it can be moved to the manually mounted root filesystem before /sbin/init is executed.

  • 3.menuconfig 中加入devtmpfs支持
  • make menuconfig-->Device Drivers-->Generic Driver Options
  • Maintain a devtmpfs filesystem to mount at /dev
  • Automount devtmpfs at /dev, after the kernel mounted the rootfs

  • 4.df -T显示devtmpfs
  • 文件系统 类型 1K-块 已用 可用 已用% 挂载点
  • /dev/sda1 ext4 31621016 14985712 15029008 50% /
  • none devtmpfs 399552 276 399276 1% /dev
  • none tmpfs 403804 24 403780 1% /dev/shm
  • none tmpfs 403804 108 403696 1% /var/run
  • none tmpfs 403804 0 403804 0% /var/lock
  • none tmpfs 403804 0 403804 0% /lib/init/rw
  • .host:/ vmhgfs 67151668 54038400 13113268 81% /mnt/hgfs
  • /dev/loop0 ext2 16119 8528 6772 56% /mnt/loop

  • 二、devtmpfs文件系统初始化
  • void __init driver_init(void)
  • {
  •     /* These are the core pieces */
  •     devtmpfs_init();//devtmpfs文件系统初始化
  •     devices_init();
  •     buses_init();
  •     classes_init();
  •     firmware_init();
  •     hypervisor_init();
  •     platform_bus_init();
  •     system_bus_init();
  •     cpu_dev_init();
  •     memory_dev_init();
  • }

  • static struct file_system_type dev_fs_type = {
  •     .name = "devtmpfs",
  •     .mount = dev_mount,
  •     .kill_sb = kill_litter_super,
  • };
  •    
  • int __init devtmpfs_init(void)
  • {
  •     int err = register_filesystem(&dev_fs_type);//注册dev_fs_type文件系统,即将dev_fs_type添加到内核全局总链表中file_systems
  •     if (err) {
  •         printk(KERN_ERR "devtmpfs: unable to register devtmpfs ""type %i\n", err);
  •         return err;
  •     }
  •     
  •     thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");//创建并启动一个内核线程devtmpfsd
  •     if (!IS_ERR(thread)) {
  •         wait_for_completion(&setup_done);//进行一个不可打断的等待,允许一个线程告诉另一个线程工作已经完成
  •     } else {
  •         err = PTR_ERR(thread);
  •         thread = NULL;
  •     }
  •     
  •     if (err) {
  •         printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
  •         unregister_filesystem(&dev_fs_type);
  •         return err;
  •     }
  •     
  •     printk(KERN_INFO "devtmpfs: initialized\n");
  •     return 0;
  • }

  • //请求创建设备节点的请求队列req结构
  • static struct req {
  •     struct req *next;
  •     struct completion done;
  •     int err;
  •     const char *name;
  •     umode_t mode;//0代表删除
  •     struct device *dev;
  • } *requests;

  • //内核线程devtmpfsd
  • static int devtmpfsd(void *p)
  • {
  •     char options[] = "mode=0755";
  •     int *err = p;
  •     
  •     *err = sys_unshare(CLONE_NEWNS);
  •     if (*err)
  •         goto out;
  •         
  •     //挂载devtmpfs文件系统
  •     //devtmpfs是待安装设备的路径名,“/”是安装点路径名,”devtmpfs“表示文件系统类型,MS_SILENT=32768,即0x8000
  •     *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options);
  •     if (*err)
  •         goto out;
  •     sys_chdir("/.."); //将进程的当前工作目录(pwd)设定为devtmpfs文件系统的根目录/* will traverse into overmounted root */
  •     sys_chroot(".");
  •     complete(&setup_done);//允许一个线程告诉另一个线程工作已经完成
  •     while (1) {
  •         spin_lock(&req_lock);
  •         while (requests) {//扫描请求链表,每当要创建一个设备节点时,都需要向requests链表中添加请求
  •             struct req *req = requests;//赋值给临时req
  •             requests = NULL;//清空
  •             spin_unlock(&req_lock);
  •             while (req) {//遍历刚才requests的请求链表
  •                 struct req *next = req->next;
  •                 req->err = handle(req->name, req->mode, req->dev);//对链表中的每一个请求调用handle函数
  •                 complete(&req->done);
  •                 req = next;
  •             }
  •             spin_lock(&req_lock);
  •         }
  •         __set_current_state(TASK_INTERRUPTIBLE);//设置为睡眠状态
  •         spin_unlock(&req_lock);
  •         schedule();//系统切换
  •     }
  •     return 0;
  • out:
  •     complete(&setup_done);
  •     return *err;
  • }

  • static int handle(const char *name, umode_t mode, struct device *dev)
  • {
  •     if (mode)
  •         return handle_create(name, mode, dev);
  •     else
  •         return handle_remove(name, dev);
  • }

  • static int handle_create(const char *nodename, umode_t mode, struct device *dev)
  • {
  •     struct dentry *dentry;
  •     struct path path;
  •     int err;
  •     
  •     //查找节点名称的路径以及返回节点对应的父目录dentry结构,即在此目录下创建一个设备节点,即是/dev目录对应的dentry结构
  •     dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
  •     if (dentry == ERR_PTR(-ENOENT)) {
  •         create_path(nodename);
  •         dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
  •     }
  •     if (IS_ERR(dentry))
  •         return PTR_ERR(dentry);
  •     
  •     //创建设备节点
  •     err = vfs_mknod(path.dentry->d_inode,dentry, mode, dev->devt);
  •     if (!err) {
  •         struct iattr newattrs;
  •         newattrs.ia_mode = mode;/* fixup possibly umasked mode */
  •         newattrs.ia_valid = ATTR_MODE;
  •         mutex_lock(&dentry->d_inode->i_mutex);
  •         notify_change(dentry, &newattrs);
  •         mutex_unlock(&dentry->d_inode->i_mutex);
  •         dentry->d_inode->i_private = &thread;/* mark as kernel-created inode */
  •     }
  •     done_path_create(&path, dentry);//与前边kern_path_create对应,减少path和dentry的计数等
  •     return err;
  • }

  • int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
  • {
  •     int error = may_create(dir, dentry);//检查是否可以创建设备文件节点
  •     
  •     if (error)
  •         return error;
  •     
  •     //必须是字符设备或者块设备,且具有创建节点的权限
  •     if ((S_ISCHR(mode) || S_ISBLK(mode)) && !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;
  •     
  •     //调用具体文件系统的mknod()函数
  •     //mount时调用shmem_fill_super()-->shmem_get_inode()分配inode节点时做出的初始化
  •     /*那么在shmem_get_inode中
  •         caseS_IFDIR:
  •         inc_nlink(inode);
  •         inode->i_size= 2 * BOGO_DIRENT_SIZE;
  •         inode->i_op= &shmem_dir_inode_operations;
  •         inode->i_fop= &simple_dir_operations;
  •         由于mountpoint是dev这个目录,所以dev对应的inode的i_op就是shmem_dir_inode_operations。
  •         staticconst struct inode_operations shmem_dir_inode_operations = {
  •             #ifdefCONFIG_TMPFS
  •             .create =shmem_create,
  •             .lookup =simple_lookup,
  •             .link =shmem_link,
  •             .unlink =shmem_unlink,
  •             .symlink =shmem_symlink,
  •             .mkdir =shmem_mkdir,
  •             .rmdir =shmem_rmdir,
  •             .mknod =shmem_mknod,
  •             .rename =shmem_rename,
  •             #endif
  •             #ifdefCONFIG_TMPFS_POSIX_ACL
  •             .setattr =shmem_notify_change,
  •             .setxattr =generic_setxattr,
  •             .getxattr =generic_getxattr,
  •             .listxattr =generic_listxattr,
  •             .removexattr =generic_removexattr,
  •             .check_acl =generic_check_acl,
  •             #endif
  •             };
  •         */
  •     error = dir->i_op->mknod(dir, dentry, mode, dev);//所以这里调用的就是shmem_mknod
  •     if (!error)
  •         fsnotify_create(dir, dentry);
  •     return error;
  • }

  • shmem_mknod(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);//获得一个要创建的设备节点的inode,并初始化
  •     if (inode) {
  •         error = security_inode_init_security(inode, dir,&dentry->d_name,shmem_initxattrs, NULL);
  •         if (error) {
  •             if (error != -EOPNOTSUPP) {
  •                 iput(inode);
  •                 return error;
  •             }
  •         }
  • #ifdef CONFIG_TMPFS_POSIX_ACL
  •         error = generic_acl_init(inode, dir);
  •         if (error) {
  •             iput(inode);
  •             return error;
  •         }
  • #else
  •         error = 0;
  • #endif
  •         dir->i_size += BOGO_DIRENT_SIZE;
  •         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  •         d_instantiate(dentry, inode);//与dentry建立关,此时就可以在/dev下看到这个字符设备节点了
  •         dget(dentry); //递减dentry的计数
  •     }
  •     return error;
  • }

  • 三、文件系统的mount
  • 内核主要是通过kernel_init调用prepare_namespace()函数执行安装实际根文件系统的操作:
  • void __init prepare_namespace(void)
  • {
  •     int is_floppy;
  •   
  •     if (root_delay) {
  •         printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
  •                root_delay);
  •         ssleep(root_delay);
  •     }
  •     wait_for_device_probe();
  •   
  •     md_run_setup();
  •     /* 把root_device_name变量置为从启动参数“root”中获取的设备文件名。
  •   * 同样,把ROOT_DEV变量置为同一设备文件的主设备号和次设备号。*/
  •     if (saved_root_name[0]) {
  •         root_device_name = saved_root_name;
  •         if (!strncmp(root_device_name, "mtd", 3) ||
  •             !strncmp(root_device_name, "ubi", 3)) {
  •             mount_block_root(root_device_name, root_mountflags);
  •             goto out;
  •         }
  •         ROOT_DEV = name_to_dev_t(root_device_name);//转换为设备号/dev/mtdblock2.
  •         if (strncmp(root_device_name, "/dev/", 5) == 0)
  •             root_device_name += 5;
  •     }
  •   
  •     if (initrd_load())
  •         goto out;
  •   
  •     /* wait for any asynchronous scanning to complete */
  •     if ((ROOT_DEV == 0) && root_wait) {
  •         printk(KERN_INFO "Waiting for root device %s...\n",
  •             saved_root_name);
  •         while (driver_probe_done() != 0 ||
  •             (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
  •             msleep(100);
  •         async_synchronize_full();
  •     }
  •   
  •     is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
  •   
  •     if (is_floppy && rd_doload && rd_load_disk(0))
  •         ROOT_DEV = Root_RAM0;
  •   
  •     mount_root();
  • out:
  •     devtmpfs_mount("dev");//挂载devtmpfs文件系统
  •     sys_mount(".", "/", NULL, MS_MOVE, NULL); /* 移动rootfs文件系统根目录上的已安装文件系统的安装点。 */
  •     sys_chroot(".");
  • }

  • int devtmpfs_mount(const char *mntdir)
  • {
  •     int err;
  •     
  •     if (!mount_dev)
  •         return 0;
  •     
  •     if (!thread)
  •         return 0;
  •     //将devtmpfs文件系统挂载到/dev目录下
  •     err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
  •     if (err)
  •         printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
  •     else
  •         printk(KERN_INFO "devtmpfs: mounted\n");
  •     return err;
  • }


  • 四、devtmpfs创建节点
  • 系统在启动过程中,扫描到的设备会通过devtmpfs_create_node()函数来添加设备节点。
  • int devtmpfs_create_node(struct device *dev)
  • {
  •     const char *tmp = NULL;
  •     struct req req;
  •     
  •     if (!thread)
  •     return 0;
  •     
  •     req.mode = 0;
  •     req.name = device_get_devnode(dev, &req.mode, &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;
  •     
  •     init_completion(&req.done);
  •     
  •     spin_lock(&req_lock);
  •     req.next = requests;//请求添加到requests链表
  •     requests = &req;
  •     spin_unlock(&req_lock);
  •     
  •     wake_up_process(thread);//唤醒内核线程devtmpfsd添加设备节点
  •     wait_for_completion(&req.done);
  •     
  •     kfree(tmp);
  •     
  •     return req.err;
  • }

  • const char *device_get_devnode(struct device *dev,umode_t *mode, const char **tmp)
  • {
  •     char *s;
  •     
  •     *tmp = NULL;
  •     
  •     /* the device type may provide a specific name */
  •     if (dev->type && dev->type->devnode)
  •         *tmp = dev->type->devnode(dev, mode);
  •     if (*tmp)
  •         return *tmp;
  •     
  •     /* the class may provide a specific name */
  •     if (dev->class && dev->class->devnode)
  •         *tmp = dev->class->devnode(dev, mode);
  •     if (*tmp)
  •         return *tmp;
  •     
  •     /* return name without allocation, tmp == NULL */
  •     if (strchr(dev_name(dev), '!') == NULL)
  •         return dev_name(dev);
  •     
  •     /* replace '!' in the name with '/' */
  •     *tmp = kstrdup(dev_name(dev), GFP_KERNEL);
  •     if (!*tmp)
  •         return NULL;
  •     while ((s = strchr(*tmp, '!')))
  •         s[0] = '/';
  •     return *tmp;
  • }
    • 本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
      转藏 分享 献花(0

      0条评论

      发表

      请遵守用户 评论公约

      类似文章 更多