devtmpfs文件系统创建设备节点

    1. 一、devtmpfs概述
    2. 1.devtmpfs 的功用是在 Linux 核心 启动早期建立一个初步的 /dev,令一般启动程序不用等待 udev,缩短 GNU/Linux 的开机时间。
    3. 2.重要解释
    4. Devtmpfs lets the kernel create a tmpfs very early at kernel initialization, before any driver core deviceis registered.Every device with a major/minor will have a device node createdin this tmpfs instance. After the rootfsis mounted by the kernel, the populated tmpfsis mounted at /dev.In initramfs, it can be movedto the manually mounted root filesystem before/sbin/init is executed.
    5. 3.menuconfig 中加入devtmpfs支持 
    6. make menuconfig-->Device Drivers-->Generic Driver Options
    7. Maintain a devtmpfs filesystem to mount at/dev
    8. Automount devtmpfs at /dev, after the kernel mounted the rootfs
    9. 4.df -T显示devtmpfs
    10. 文件系统 类型 1K-块 已用 可用 已用% 挂载点
    11. /dev/sda1 ext4 31621016 14985712 15029008 50%/
    12. none devtmpfs 399552 276 399276 1% /dev
    13. none tmpfs 403804 24 403780 1% /dev/shm
    14. none tmpfs 403804 108 403696 1% /var/run
    15. none tmpfs 403804 0 403804 0% /var/lock
    16. none tmpfs 403804 0 403804 0% /lib/init/rw
    17. .host:/ vmhgfs 67151668 54038400 13113268 81%/mnt/hgfs
    18. /dev/loop0 ext2 16119 8528 6772 56%/mnt/loop
    19. 二、devtmpfs文件系统初始化
      1.  
        void __init driver_init(void)
      2.  
        {
      3.  
        /* These are the core pieces*/
      4.  
        devtmpfs_init();//devtmpfs文件系统初始化
      5.  
        devices_init();
      6.  
        buses_init();
      7.  
        classes_init();
      8.  
        firmware_init();
      9.  
        hypervisor_init();
      10.  
        platform_bus_init();
      11.  
        system_bus_init();
      12.  
        cpu_dev_init();
      13.  
        memory_dev_init();
      14.  
        }
      1.  
        static struct file_system_type dev_fs_type ={
      2.  
        .name ="devtmpfs",
      3.  
        .mount = dev_mount,
      4.  
        .kill_sb = kill_litter_super,
      5.  
        };
      1.  
         
      2.  
        int __init devtmpfs_init(void)
      3.  
        {
      4.  
        int err= register_filesystem(&dev_fs_type);//注册dev_fs_type文件系统,即将dev_fs_type添加到内核全局总链表中file_systems
      5.  
        if (err){
      6.  
        printk(KERN_ERR "devtmpfs: unable to register devtmpfs ""type %i\n",err);
      7.  
        return err;
      8.  
        }
      9.  
         
      10.  
        thread = kthread_run(devtmpfsd,&err,"kdevtmpfs");//创建并启动一个内核线程devtmpfsd
      11.  
        if (!IS_ERR(thread)){
      12.  
        wait_for_completion(&setup_done);//进行一个不可打断的等待,允许一个线程告诉另一个线程工作已经完成
      13.  
        } else{
      14.  
        err = PTR_ERR(thread);
      15.  
        thread = NULL;
      16.  
        }
      17.  
         
      18.  
        if (err){
      19.  
        printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n",err);
      20.  
        unregister_filesystem(&dev_fs_type);
      21.  
        return err;
      22.  
        }
      23.  
         
      24.  
        printk(KERN_INFO "devtmpfs: initialized\n");
      25.  
        return 0;
      26.  
        }


    20. //请求创建设备节点的请求队列req结构
      1.  
        static struct req {
      2.  
        struct req *next;
      3.  
        struct completion done;
      4.  
        int err;
      5.  
        const char *name;
      6.  
        umode_t mode;//0代表删除
      7.  
        struct device *dev;
      8.  
        } *requests;


    21. //内核线程devtmpfsd
      1.  
        static int devtmpfsd(void*p)
      2.  
        {
      3.  
        char options[]= "mode=0755";
      4.  
        int *err= p;
      5.  
         
      6.  
        *err= sys_unshare(CLONE_NEWNS);
      7.  
        if (*err)
      8.  
        goto out;
      9.  
         
      10.  
        //挂载devtmpfs文件系统
      11.  
        //devtmpfs是待安装设备的路径名,“/”是安装点路径名,”devtmpfs“表示文件系统类型,MS_SILENT=32768,即0x8000
      12.  
        *err= sys_mount("devtmpfs","/", "devtmpfs", MS_SILENT, options);
      13.  
        if (*err)
      14.  
        goto out;
      15.  
        sys_chdir("/..");//将进程的当前工作目录(pwd)设定为devtmpfs文件系统的根目录/* will traverse into overmounted root */
      16.  
        sys_chroot(".");
      17.  
        complete(&setup_done);//允许一个线程告诉另一个线程工作已经完成
      18.  
        while (1){
      19.  
        spin_lock(&req_lock);
      20.  
        while (requests) {//扫描请求链表,每当要创建一个设备节点时,都需要向requests链表中添加请求
      21.  
        struct req *req = requests;//赋值给临时req
      22.  
        requests = NULL;//清空
      23.  
        spin_unlock(&req_lock);
      24.  
        while (req) {//遍历刚才requests的请求链表
      25.  
        struct req *next= req->next;
      26.  
        req->err= handle(req->name, req->mode, req->dev);//对链表中的每一个请求调用handle函数
      27.  
        complete(&req->done);
      28.  
        req = next;
      29.  
        }
      30.  
        spin_lock(&req_lock);
      31.  
        }
      32.  
        __set_current_state(TASK_INTERRUPTIBLE);//设置为睡眠状态
      33.  
        spin_unlock(&req_lock);
      34.  
        schedule();//系统切换
      35.  
        }
      36.  
        return 0;
      37.  
        out:
      38.  
        complete(&setup_done);
      39.  
        return *err;
      40.  
        }


      1.  
        static int handle(const char*name, umode_t mode, struct device*dev)
      2.  
        {
      3.  
        if (mode)
      4.  
        return handle_create(name, mode, dev);
      5.  
        else
      6.  
        return handle_remove(name, dev);
      7.  
        }


      1.  
        static int handle_create(const char*nodename, umode_t mode, struct device*dev)
      2.  
        {
      3.  
        struct dentry *dentry;
      4.  
        struct path path;
      5.  
        int err;
      6.  
         
      7.  
        //查找节点名称的路径以及返回节点对应的父目录dentry结构,即在此目录下创建一个设备节点,即是/dev目录对应的dentry结构
      8.  
        dentry = kern_path_create(AT_FDCWD, nodename,&path, 0);
      9.  
        if (dentry== ERR_PTR(-ENOENT)){
      10.  
        create_path(nodename);
      11.  
        dentry = kern_path_create(AT_FDCWD, nodename,&path, 0);
      12.  
        }
      13.  
        if (IS_ERR(dentry))
      14.  
        return PTR_ERR(dentry);
      15.  
         
      16.  
        //创建设备节点
      17.  
        err = vfs_mknod(path.dentry->d_inode,dentry, mode, dev->devt);
      18.  
        if (!err){
      19.  
        struct iattr newattrs;
      20.  
        newattrs.ia_mode = mode;/* fixup possibly umasked mode*/
      21.  
        newattrs.ia_valid = ATTR_MODE;
      22.  
        mutex_lock(&dentry->d_inode->i_mutex);
      23.  
        notify_change(dentry,&newattrs);
      24.  
        mutex_unlock(&dentry->d_inode->i_mutex);
      25.  
        dentry->d_inode->i_private= &thread;/* mark as kernel-created inode*/
      26.  
        }
      27.  
        done_path_create(&path, dentry);//与前边kern_path_create对应,减少path和dentry的计数等
      28.  
        return err;
      29.  
        }


      1.  
        int vfs_mknod(struct inode*dir, struct dentry*dentry, umode_t mode, dev_t dev)
      2.  
        {
      3.  
        int error= may_create(dir, dentry);//检查是否可以创建设备文件节点
      4.  
         
      5.  
        if (error)
      6.  
        return error;
      7.  
         
      8.  
        //必须是字符设备或者块设备,且具有创建节点的权限
      9.  
        if ((S_ISCHR(mode)|| S_ISBLK(mode))&& !capable(CAP_MKNOD))
      10.  
        return -EPERM;
      11.  
         
      12.  
        if (!dir->i_op->mknod)
      13.  
        return -EPERM;
      14.  
         
      15.  
        error = devcgroup_inode_mknod(mode, dev);
      16.  
        if (error)
      17.  
        return error;
      18.  
         
      19.  
        error = security_inode_mknod(dir, dentry, mode, dev);
      20.  
        if (error)
      21.  
        return error;
      22.  
         
      23.  
        //调用具体文件系统的mknod()函数
      24.  
        //mount时调用shmem_fill_super()-->shmem_get_inode()分配inode节点时做出的初始化
      25.  
        /*那么在shmem_get_inode中
      26.  
        caseS_IFDIR:
      27.  
        inc_nlink(inode);
      28.  
        inode->i_size= 2* BOGO_DIRENT_SIZE;
      29.  
        inode->i_op=&shmem_dir_inode_operations;
      30.  
        inode->i_fop=&simple_dir_operations;
      31.  
        由于mountpoint是dev这个目录,所以dev对应的inode的i_op就是shmem_dir_inode_operations。
      32.  
        staticconst struct inode_operations shmem_dir_inode_operations = {
      33.  
        #ifdefCONFIG_TMPFS
      34.  
        .create =shmem_create,
      35.  
        .lookup =simple_lookup,
      36.  
        .link=shmem_link,
      37.  
        .unlink =shmem_unlink,
      38.  
        .symlink =shmem_symlink,
      39.  
        .mkdir =shmem_mkdir,
      40.  
        .rmdir =shmem_rmdir,
      41.  
        .mknod =shmem_mknod,
      42.  
        .rename =shmem_rename,
      43.  
        #endif
      44.  
        #ifdefCONFIG_TMPFS_POSIX_ACL
      45.  
        .setattr =shmem_notify_change,
      46.  
        .setxattr =generic_setxattr,
      47.  
        .getxattr =generic_getxattr,
      48.  
        .listxattr =generic_listxattr,
      49.  
        .removexattr =generic_removexattr,
      50.  
        .check_acl =generic_check_acl,
      51.  
        #endif
      52.  
        };
      53.  
        */
      54.  
        error = dir->i_op->mknod(dir, dentry, mode, dev);//所以这里调用的就是shmem_mknod
      55.  
        if (!error)
      56.  
        fsnotify_create(dir, dentry);
      57.  
        return error;
      58.  
        }


      1.  
        shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
      2.  
        {
      3.  
        struct inode *inode;
      4.  
        int error= -ENOSPC;
      5.  
         
      6.  
        inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);//获得一个要创建的设备节点的inode,并初始化
      7.  
        if (inode){
      8.  
        error = security_inode_init_security(inode, dir,&dentry->d_name,shmem_initxattrs,NULL);
      9.  
        if (error){
      10.  
        if (error !=-EOPNOTSUPP){
      11.  
        iput(inode);
      12.  
        return error;
      13.  
        }
      14.  
        }
      15.  
        #ifdef CONFIG_TMPFS_POSIX_ACL
      16.  
        error = generic_acl_init(inode, dir);
      17.  
        if (error){
      18.  
        iput(inode);
      19.  
        return error;
      20.  
        }
      21.  
        #else
      22.  
        error = 0;
      23.  
        #endif
      24.  
        dir->i_size+= BOGO_DIRENT_SIZE;
      25.  
        dir->i_ctime= dir->i_mtime= CURRENT_TIME;
      26.  
        d_instantiate(dentry, inode);//与dentry建立关,此时就可以在/dev下看到这个字符设备节点了
      27.  
        dget(dentry);//递减dentry的计数
      28.  
        }
      29.  
        return error;
      30.  
        }


    22. 三、文件系统的mount
    23. 内核主要是通过kernel_init调用prepare_namespace()函数执行安装实际根文件系统的操作:
      1.  
        void __init prepare_namespace(void)
      2.  
        {
      3.  
        int is_floppy;
      4.  
         
      5.  
        if (root_delay){
      6.  
        printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
      7.  
        root_delay);
      8.  
        ssleep(root_delay);
      9.  
        }
      10.  
        wait_for_device_probe();
      11.  
         
      12.  
        md_run_setup();
      13.  
        /* 把root_device_name变量置为从启动参数“root”中获取的设备文件名。
      14.  
        * 同样,把ROOT_DEV变量置为同一设备文件的主设备号和次设备号。*/
      15.  
        if (saved_root_name[0]){
      16.  
        root_device_name = saved_root_name;
      17.  
        if (!strncmp(root_device_name,"mtd", 3)||
      18.  
        !strncmp(root_device_name,"ubi", 3)){
      19.  
        mount_block_root(root_device_name, root_mountflags);
      20.  
        goto out;
      21.  
        }
      22.  
        ROOT_DEV = name_to_dev_t(root_device_name);//转换为设备号/dev/mtdblock2.
      23.  
        if (strncmp(root_device_name,"/dev/", 5)== 0)
      24.  
        root_device_name += 5;
      25.  
        }
      26.  
         
      27.  
        if (initrd_load())
      28.  
        goto out;
      29.  
         
      30.  
        /* waitfor any asynchronous scanning to complete */
      31.  
        if ((ROOT_DEV== 0)&& root_wait){
      32.  
        printk(KERN_INFO "Waiting for root device %s...\n",
      33.  
        saved_root_name);
      34.  
        while (driver_probe_done()!= 0 ||
      35.  
        (ROOT_DEV = name_to_dev_t(saved_root_name))== 0)
      36.  
        msleep(100);
      37.  
        async_synchronize_full();
      38.  
        }
      39.  
         
      40.  
        is_floppy = MAJOR(ROOT_DEV)== FLOPPY_MAJOR;
      41.  
         
      42.  
        if (is_floppy&& rd_doload&& rd_load_disk(0))
      43.  
        ROOT_DEV = Root_RAM0;
      44.  
         
      45.  
        mount_root();
      46.  
        out:
      47.  
        devtmpfs_mount("dev");//挂载devtmpfs文件系统
      48.  
        sys_mount(".","/", NULL, MS_MOVE,NULL);/* 移动rootfs文件系统根目录上的已安装文件系统的安装点。*/
      49.  
        sys_chroot(".");
      50.  
        }


      1.  
        int devtmpfs_mount(const char*mntdir)
      2.  
        {
      3.  
        int err;
      4.  
         
      5.  
        if (!mount_dev)
      6.  
        return 0;
      7.  
         
      8.  
        if (!thread)
      9.  
        return 0;
      10.  
        //将devtmpfs文件系统挂载到/dev目录下
      11.  
        err = sys_mount("devtmpfs",(char *)mntdir,"devtmpfs", MS_SILENT,NULL);
      12.  
        if (err)
      13.  
        printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
      14.  
        else
      15.  
        printk(KERN_INFO "devtmpfs: mounted\n");
      16.  
        return err;
      17.  
        }


    24. 四、devtmpfs创建节点
    25. 系统在启动过程中,扫描到的设备会通过devtmpfs_create_node()函数来添加设备节点。
      1.  
        int devtmpfs_create_node(struct device*dev)
      2.  
        {
      3.  
        const char *tmp = NULL;
      4.  
        struct req req;
      5.  
         
      6.  
        if (!thread)
      7.  
        return 0;
      8.  
         
      9.  
        req.mode = 0;
      10.  
        req.name = device_get_devnode(dev,&req.mode,&tmp);//获得设备名
      11.  
        if (!req.name)
      12.  
        return -ENOMEM;
      13.  
         
      14.  
        if (req.mode== 0)
      15.  
        req.mode = 0600;
      16.  
        if (is_blockdev(dev))
      17.  
        req.mode |= S_IFBLK;//块设备
      18.  
        else
      19.  
        req.mode |= S_IFCHR;//字符设备
      20.  
         
      21.  
        req.dev = dev;
      22.  
         
      23.  
        init_completion(&req.done);
      24.  
         
      25.  
        spin_lock(&req_lock);
      26.  
        req.next= requests;//请求添加到requests链表
      27.  
        requests = &req;
      28.  
        spin_unlock(&req_lock);
      29.  
         
      30.  
        wake_up_process(thread);//唤醒内核线程devtmpfsd添加设备节点
      31.  
        wait_for_completion(&req.done);
      32.  
         
      33.  
        kfree(tmp);
      34.  
         
      35.  
        return req.err;
      36.  
        }


      1.  
        const char *device_get_devnode(struct device*dev,umode_t*mode, const char **tmp)
      2.  
        {
      3.  
        char *s;
      4.  
         
      5.  
        *tmp =NULL;
      6.  
         
      7.  
        /* the device type may provide a specific name*/
      8.  
        if (dev->type&& dev->type->devnode)
      9.  
        *tmp = dev->type->devnode(dev, mode);
      10.  
        if (*tmp)
      11.  
        return *tmp;
      12.  
         
      13.  
        /* theclass may provide a specific name */
      14.  
        if (dev->class&& dev->class->devnode)
      15.  
        *tmp = dev->class->devnode(dev, mode);
      16.  
        if (*tmp)
      17.  
        return *tmp;
      18.  
         
      19.  
        /* return name without allocation, tmp== NULL */
      20.  
        if (strchr(dev_name(dev),'!')== NULL)
      21.  
        return dev_name(dev);
      22.  
         
      23.  
        /* replace '!'in the name with '/'*/
      24.  
        *tmp = kstrdup(dev_name(dev), GFP_KERNEL);
      25.  
        if (!*tmp)
      26.  
        return NULL;
      27.  
        while ((s= strchr(*tmp,'!')))
      28.  
        s[0]= '/';
      29.  
        return *tmp;
      30.  
        }
      31.  

posted on 2019-08-15 14:44  guanxi0808  阅读(1219)  评论(0)    收藏  举报

导航