devtmpfs文件系统创建设备节点

来源:互联网 发布:淘宝爽肤水有什么用 编辑:程序博客网 时间:2024/05/13 06:51
  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/initis 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;} 

0 0
原创粉丝点击