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文件系统初始化
  20. 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();
    }
    


  21. 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;
    }



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


  24. //内核线程devtmpfsd
  25. 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;
    }


  26. 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);
    }


  27. 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;
    }


  28. 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;
    }


  29. 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;
    }


  30. 三、文件系统的mount
  31. 内核主要是通过kernel_init调用prepare_namespace()函数执行安装实际根文件系统的操作:
  32. 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;
      
        /* waitfor 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(".");
    } 


  33. 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;
    }



  34. 四、devtmpfs创建节点
  35. 系统在启动过程中,扫描到的设备会通过devtmpfs_create_node()函数来添加设备节点。
  36. 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;
        
        /* theclass 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;
    } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值