• devtmpfs文件系统创建设备节点


    1. 分类: LINUX

      原文地址:devtmpfs文件系统创建设备节点 作者:wangbaolin719

    2. http://blog.chinaunix.net/uid-27097876-id-4334356.html
    3. 一、devtmpfs概述
    4. 1.devtmpfs 的功用是在 Linux 核心 启动早期建立一个初步的 /dev,令一般启动程序不用等待 udev,缩短 GNU/Linux 的开机时间。

    5. 2.重要解释
    6. 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.

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

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

    21. 二、devtmpfs文件系统初始化
    22. void __init driver_init(void)
    23. {
    24.     /* These are the core pieces */
    25.     devtmpfs_init();//devtmpfs文件系统初始化
    26.     devices_init();
    27.     buses_init();
    28.     classes_init();
    29.     firmware_init();
    30.     hypervisor_init();
    31.     platform_bus_init();
    32.     system_bus_init();
    33.     cpu_dev_init();
    34.     memory_dev_init();
    35. }

    36. static struct file_system_type dev_fs_type = {
    37.     .name = "devtmpfs",
    38.     .mount = dev_mount,
    39.     .kill_sb = kill_litter_super,
    40. };
    41.    
    42. int __init devtmpfs_init(void)
    43. {
    44.     int err = register_filesystem(&dev_fs_type);//注册dev_fs_type文件系统,即将dev_fs_type添加到内核全局总链表中file_systems
    45.     if (err) {
    46.         printk(KERN_ERR "devtmpfs: unable to register devtmpfs ""type %i ", err);
    47.         return err;
    48.     }
    49.     
    50.     thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");//创建并启动一个内核线程devtmpfsd
    51.     if (!IS_ERR(thread)) {
    52.         wait_for_completion(&setup_done);//进行一个不可打断的等待,允许一个线程告诉另一个线程工作已经完成
    53.     } else {
    54.         err = PTR_ERR(thread);
    55.         thread = NULL;
    56.     }
    57.     
    58.     if (err) {
    59.         printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i ", err);
    60.         unregister_filesystem(&dev_fs_type);
    61.         return err;
    62.     }
    63.     
    64.     printk(KERN_INFO "devtmpfs: initialized ");
    65.     return 0;
    66. }

    67. //请求创建设备节点的请求队列req结构
    68. static struct req {
    69.     struct req *next;
    70.     struct completion done;
    71.     int err;
    72.     const char *name;
    73.     umode_t mode;//0代表删除
    74.     struct device *dev;
    75. } *requests;

    76. //内核线程devtmpfsd
    77. static int devtmpfsd(void *p)
    78. {
    79.     char options[] = "mode=0755";
    80.     int *err = p;
    81.     
    82.     *err = sys_unshare(CLONE_NEWNS);
    83.     if (*err)
    84.         goto out;
    85.         
    86.     //挂载devtmpfs文件系统
    87.     //devtmpfs是待安装设备的路径名,“/”是安装点路径名,”devtmpfs“表示文件系统类型,MS_SILENT=32768,即0x8000
    88.     *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options);
    89.     if (*err)
    90.         goto out;
    91.     sys_chdir("/.."); //将进程的当前工作目录(pwd)设定为devtmpfs文件系统的根目录/* will traverse into overmounted root */
    92.     sys_chroot(".");
    93.     complete(&setup_done);//允许一个线程告诉另一个线程工作已经完成
    94.     while (1) {
    95.         spin_lock(&req_lock);
    96.         while (requests) {//扫描请求链表,每当要创建一个设备节点时,都需要向requests链表中添加请求
    97.             struct req *req = requests;//赋值给临时req
    98.             requests = NULL;//清空
    99.             spin_unlock(&req_lock);
    100.             while (req) {//遍历刚才requests的请求链表
    101.                 struct req *next = req->next;
    102.                 req->err = handle(req->name, req->mode, req->dev);//对链表中的每一个请求调用handle函数
    103.                 complete(&req->done);
    104.                 req = next;
    105.             }
    106.             spin_lock(&req_lock);
    107.         }
    108.         __set_current_state(TASK_INTERRUPTIBLE);//设置为睡眠状态
    109.         spin_unlock(&req_lock);
    110.         schedule();//系统切换
    111.     }
    112.     return 0;
    113. out:
    114.     complete(&setup_done);
    115.     return *err;
    116. }

    117. static int handle(const char *name, umode_t mode, struct device *dev)
    118. {
    119.     if (mode)
    120.         return handle_create(name, mode, dev);
    121.     else
    122.         return handle_remove(name, dev);
    123. }

    124. static int handle_create(const char *nodename, umode_t mode, struct device *dev)
    125. {
    126.     struct dentry *dentry;
    127.     struct path path;
    128.     int err;
    129.     
    130.     //查找节点名称的路径以及返回节点对应的父目录dentry结构,即在此目录下创建一个设备节点,即是/dev目录对应的dentry结构
    131.     dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
    132.     if (dentry == ERR_PTR(-ENOENT)) {
    133.         create_path(nodename);
    134.         dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
    135.     }
    136.     if (IS_ERR(dentry))
    137.         return PTR_ERR(dentry);
    138.     
    139.     //创建设备节点
    140.     err = vfs_mknod(path.dentry->d_inode,dentry, mode, dev->devt);
    141.     if (!err) {
    142.         struct iattr newattrs;
    143.         newattrs.ia_mode = mode;/* fixup possibly umasked mode */
    144.         newattrs.ia_valid = ATTR_MODE;
    145.         mutex_lock(&dentry->d_inode->i_mutex);
    146.         notify_change(dentry, &newattrs);
    147.         mutex_unlock(&dentry->d_inode->i_mutex);
    148.         dentry->d_inode->i_private = &thread;/* mark as kernel-created inode */
    149.     }
    150.     done_path_create(&path, dentry);//与前边kern_path_create对应,减少path和dentry的计数等
    151.     return err;
    152. }

    153. int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
    154. {
    155.     int error = may_create(dir, dentry);//检查是否可以创建设备文件节点
    156.     
    157.     if (error)
    158.         return error;
    159.     
    160.     //必须是字符设备或者块设备,且具有创建节点的权限
    161.     if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
    162.         return -EPERM;
    163.     
    164.     if (!dir->i_op->mknod)
    165.         return -EPERM;
    166.     
    167.     error = devcgroup_inode_mknod(mode, dev);
    168.     if (error)
    169.         return error;
    170.     
    171.     error = security_inode_mknod(dir, dentry, mode, dev);
    172.     if (error)
    173.         return error;
    174.     
    175.     //调用具体文件系统的mknod()函数
    176.     //mount时调用shmem_fill_super()-->shmem_get_inode()分配inode节点时做出的初始化
    177.     /*那么在shmem_get_inode中
    178.         caseS_IFDIR:
    179.         inc_nlink(inode);
    180.         inode->i_size= 2 * BOGO_DIRENT_SIZE;
    181.         inode->i_op= &shmem_dir_inode_operations;
    182.         inode->i_fop= &simple_dir_operations;
    183.         由于mountpoint是dev这个目录,所以dev对应的inode的i_op就是shmem_dir_inode_operations。
    184.         staticconst struct inode_operations shmem_dir_inode_operations = {
    185.             #ifdefCONFIG_TMPFS
    186.             .create =shmem_create,
    187.             .lookup =simple_lookup,
    188.             .link =shmem_link,
    189.             .unlink =shmem_unlink,
    190.             .symlink =shmem_symlink,
    191.             .mkdir =shmem_mkdir,
    192.             .rmdir =shmem_rmdir,
    193.             .mknod =shmem_mknod,
    194.             .rename =shmem_rename,
    195.             #endif
    196.             #ifdefCONFIG_TMPFS_POSIX_ACL
    197.             .setattr =shmem_notify_change,
    198.             .setxattr =generic_setxattr,
    199.             .getxattr =generic_getxattr,
    200.             .listxattr =generic_listxattr,
    201.             .removexattr =generic_removexattr,
    202.             .check_acl =generic_check_acl,
    203.             #endif
    204.             };
    205.         */
    206.     error = dir->i_op->mknod(dir, dentry, mode, dev);//所以这里调用的就是shmem_mknod
    207.     if (!error)
    208.         fsnotify_create(dir, dentry);
    209.     return error;
    210. }

    211. shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
    212. {
    213.     struct inode *inode;
    214.     int error = -ENOSPC;
    215.     
    216.     inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);//获得一个要创建的设备节点的inode,并初始化
    217.     if (inode) {
    218.         error = security_inode_init_security(inode, dir,&dentry->d_name,shmem_initxattrs, NULL);
    219.         if (error) {
    220.             if (error != -EOPNOTSUPP) {
    221.                 iput(inode);
    222.                 return error;
    223.             }
    224.         }
    225. #ifdef CONFIG_TMPFS_POSIX_ACL
    226.         error = generic_acl_init(inode, dir);
    227.         if (error) {
    228.             iput(inode);
    229.             return error;
    230.         }
    231. #else
    232.         error = 0;
    233. #endif
    234.         dir->i_size += BOGO_DIRENT_SIZE;
    235.         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
    236.         d_instantiate(dentry, inode);//与dentry建立关,此时就可以在/dev下看到这个字符设备节点了
    237.         dget(dentry); //递减dentry的计数
    238.     }
    239.     return error;
    240. }

    241. 三、文件系统的mount
    242. 内核主要是通过kernel_init调用prepare_namespace()函数执行安装实际根文件系统的操作:
    243. void __init prepare_namespace(void)
    244. {
    245.     int is_floppy;
    246.   
    247.     if (root_delay) {
    248.         printk(KERN_INFO "Waiting %dsec before mounting root device... ",
    249.                root_delay);
    250.         ssleep(root_delay);
    251.     }
    252.     wait_for_device_probe();
    253.   
    254.     md_run_setup();
    255.     /* 把root_device_name变量置为从启动参数“root”中获取的设备文件名。
    256.   * 同样,把ROOT_DEV变量置为同一设备文件的主设备号和次设备号。*/
    257.     if (saved_root_name[0]) {
    258.         root_device_name = saved_root_name;
    259.         if (!strncmp(root_device_name, "mtd", 3) ||
    260.             !strncmp(root_device_name, "ubi", 3)) {
    261.             mount_block_root(root_device_name, root_mountflags);
    262.             goto out;
    263.         }
    264.         ROOT_DEV = name_to_dev_t(root_device_name);//转换为设备号/dev/mtdblock2.
    265.         if (strncmp(root_device_name, "/dev/", 5) == 0)
    266.             root_device_name += 5;
    267.     }
    268.   
    269.     if (initrd_load())
    270.         goto out;
    271.   
    272.     /* wait for any asynchronous scanning to complete */
    273.     if ((ROOT_DEV == 0) && root_wait) {
    274.         printk(KERN_INFO "Waiting for root device %s... ",
    275.             saved_root_name);
    276.         while (driver_probe_done() != 0 ||
    277.             (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
    278.             msleep(100);
    279.         async_synchronize_full();
    280.     }
    281.   
    282.     is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
    283.   
    284.     if (is_floppy && rd_doload && rd_load_disk(0))
    285.         ROOT_DEV = Root_RAM0;
    286.   
    287.     mount_root();
    288. out:
    289.     devtmpfs_mount("dev");//挂载devtmpfs文件系统
    290.     sys_mount(".", "/", NULL, MS_MOVE, NULL); /* 移动rootfs文件系统根目录上的已安装文件系统的安装点。 */
    291.     sys_chroot(".");
    292. }

    293. int devtmpfs_mount(const char *mntdir)
    294. {
    295.     int err;
    296.     
    297.     if (!mount_dev)
    298.         return 0;
    299.     
    300.     if (!thread)
    301.         return 0;
    302.     //将devtmpfs文件系统挂载到/dev目录下
    303.     err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
    304.     if (err)
    305.         printk(KERN_INFO "devtmpfs: error mounting %i ", err);
    306.     else
    307.         printk(KERN_INFO "devtmpfs: mounted ");
    308.     return err;
    309. }


    310. 四、devtmpfs创建节点
    311. 系统在启动过程中,扫描到的设备会通过devtmpfs_create_node()函数来添加设备节点。
    312. int devtmpfs_create_node(struct device *dev)
    313. {
    314.     const char *tmp = NULL;
    315.     struct req req;
    316.     
    317.     if (!thread)
    318.     return 0;
    319.     
    320.     req.mode = 0;
    321.     req.name = device_get_devnode(dev, &req.mode, &tmp);//获得设备名
    322.     if (!req.name)
    323.         return -ENOMEM;
    324.     
    325.     if (req.mode == 0)
    326.         req.mode = 0600;
    327.     if (is_blockdev(dev))
    328.         req.mode |= S_IFBLK;//块设备
    329.     else
    330.         req.mode |= S_IFCHR;//字符设备
    331.     
    332.     req.dev = dev;
    333.     
    334.     init_completion(&req.done);
    335.     
    336.     spin_lock(&req_lock);
    337.     req.next = requests;//请求添加到requests链表
    338.     requests = &req;
    339.     spin_unlock(&req_lock);
    340.     
    341.     wake_up_process(thread);//唤醒内核线程devtmpfsd添加设备节点
    342.     wait_for_completion(&req.done);
    343.     
    344.     kfree(tmp);
    345.     
    346.     return req.err;
    347. }

    348. const char *device_get_devnode(struct device *dev,umode_t *mode, const char **tmp)
    349. {
    350.     char *s;
    351.     
    352.     *tmp = NULL;
    353.     
    354.     /* the device type may provide a specific name */
    355.     if (dev->type && dev->type->devnode)
    356.         *tmp = dev->type->devnode(dev, mode);
    357.     if (*tmp)
    358.         return *tmp;
    359.     
    360.     /* the class may provide a specific name */
    361.     if (dev->class && dev->class->devnode)
    362.         *tmp = dev->class->devnode(dev, mode);
    363.     if (*tmp)
    364.         return *tmp;
    365.     
    366.     /* return name without allocation, tmp == NULL */
    367.     if (strchr(dev_name(dev), '!') == NULL)
    368.         return dev_name(dev);
    369.     
    370.     /* replace '!' in the name with '/' */
    371.     *tmp = kstrdup(dev_name(dev), GFP_KERNEL);
    372.     if (!*tmp)
    373.         return NULL;
    374.     while ((s = strchr(*tmp, '!')))
    375.         s[0] = '/';
    376.     return *tmp;
    377. }
  • 相关阅读:
    java实现猜生日
    java实现猜生日
    java实现猜生日
    自定义EL表达式,将对象转成json格式,关键代码
    Ajax提交post请求返回404错误
    spring-boot | 整合通用Mabatis 分页插件PageHelper
    公众号开发 jsp中<a>问题
    SpringBoot+Mybatis+ Druid+PageHelper 实现多数据源并分页
    纯JSP实现简单微信开发后台
    localStorage,sessionStorage,cookie使用场景和区别
  • 原文地址:https://www.cnblogs.com/mull/p/4801282.html
Copyright © 2020-2023  润新知