devtmpfs文件系统创建设备节点

来源:互联网 发布:php nginx 编辑:程序博客网 时间:2024/05/21 17:07

devtmpfs概述


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

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

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


devtmpfs文件系统初始化

[cpp] view plain copy
  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. }  
[cpp] view plain copy
  1. static struct file_system_type dev_fs_type = {  
  2.     .name = "devtmpfs",  
  3.     .mount = dev_mount,  
  4.     .kill_sb = kill_litter_super,  
  5. };  
[cpp] view plain copy
  1. int __init devtmpfs_init(void)  
  2. {  
  3.     int err = register_filesystem(&dev_fs_type);//注册dev_fs_type文件系统,即将dev_fs_type添加到内核全局总链表中file_systems  
  4.     if (err) {  
  5.         printk(KERN_ERR "devtmpfs: unable to register devtmpfs ""type %i\n", err);  
  6.         return err;  
  7.     }  
  8.       
  9.     thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");//创建并启动一个内核线程devtmpfsd  
  10.     if (!IS_ERR(thread)) {  
  11.         wait_for_completion(&setup_done);//进行一个不可打断的等待,允许一个线程告诉另一个线程工作已经完成  
  12.     } else {  
  13.         err = PTR_ERR(thread);  
  14.         thread = NULL;  
  15.     }  
  16.       
  17.     if (err) {  
  18.         printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);  
  19.         unregister_filesystem(&dev_fs_type);  
  20.         return err;  
  21.     }  
  22.       
  23.     printk(KERN_INFO "devtmpfs: initialized\n");  
  24.     return 0;  
  25. }  
请求创建设备节点的请求队列req结构
[cpp] view plain copy
  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;  
内核线程devtmpfsd
[cpp] view plain copy
  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. }  
[cpp] view plain copy
  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. }  
  8.   
  9. static int handle_create(const char *nodename, umode_t mode, struct device *dev)  
  10. {  
  11.     struct dentry *dentry;  
  12.     struct path path;  
  13.     int err;  
  14.       
  15.     //查找节点名称的路径以及返回节点对应的父目录dentry结构,即在此目录下创建一个设备节点,即是/dev目录对应的dentry结构  
  16.     dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);  
  17.     if (dentry == ERR_PTR(-ENOENT)) {  
  18.         create_path(nodename);  
  19.         dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);  
  20.     }  
  21.     if (IS_ERR(dentry))  
  22.         return PTR_ERR(dentry);  
  23.       
  24.     //创建设备节点  
  25.     err = vfs_mknod(path.dentry->d_inode,dentry, mode, dev->devt);  
  26.     if (!err) {  
  27.         struct iattr newattrs;  
  28.         newattrs.ia_mode = mode;/* fixup possibly umasked mode */  
  29.         newattrs.ia_valid = ATTR_MODE;  
  30.         mutex_lock(&dentry->d_inode->i_mutex);  
  31.         notify_change(dentry, &newattrs);  
  32.         mutex_unlock(&dentry->d_inode->i_mutex);  
  33.         dentry->d_inode->i_private = &thread;/* mark as kernel-created inode */  
  34.     }  
  35.     done_path_create(&path, dentry);//与前边kern_path_create对应,减少path和dentry的计数等  
  36.     return err;  
  37. }  
  38.   
  39. int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)  
  40. {  
  41.     int error = may_create(dir, dentry);//检查是否可以创建设备文件节点  
  42.       
  43.     if (error)  
  44.         return error;  
  45.       
  46.     //必须是字符设备或者块设备,且具有创建节点的权限  
  47.     if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))  
  48.         return -EPERM;  
  49.       
  50.     if (!dir->i_op->mknod)  
  51.         return -EPERM;  
  52.       
  53.     error = devcgroup_inode_mknod(mode, dev);  
  54.     if (error)  
  55.         return error;  
  56.       
  57.     error = security_inode_mknod(dir, dentry, mode, dev);  
  58.     if (error)  
  59.         return error;  
  60.       
  61.     //调用具体文件系统的mknod()函数  
  62.     //mount时调用shmem_fill_super()-->shmem_get_inode()分配inode节点时做出的初始化  
  63.     /*那么在shmem_get_inode中 
  64.         caseS_IFDIR: 
  65.         inc_nlink(inode); 
  66.         inode->i_size= 2 * BOGO_DIRENT_SIZE; 
  67.         inode->i_op= &shmem_dir_inode_operations; 
  68.         inode->i_fop= &simple_dir_operations; 
  69.         由于mountpoint是dev这个目录,所以dev对应的inode的i_op就是shmem_dir_inode_operations。 
  70.         staticconst struct inode_operations shmem_dir_inode_operations = { 
  71.             #ifdefCONFIG_TMPFS 
  72.             .create =shmem_create, 
  73.             .lookup =simple_lookup, 
  74.             .link =shmem_link, 
  75.             .unlink =shmem_unlink, 
  76.             .symlink =shmem_symlink, 
  77.             .mkdir =shmem_mkdir, 
  78.             .rmdir =shmem_rmdir, 
  79.             .mknod =shmem_mknod, 
  80.             .rename =shmem_rename, 
  81.             #endif 
  82.             #ifdefCONFIG_TMPFS_POSIX_ACL 
  83.             .setattr =shmem_notify_change, 
  84.             .setxattr =generic_setxattr, 
  85.             .getxattr =generic_getxattr, 
  86.             .listxattr =generic_listxattr, 
  87.             .removexattr =generic_removexattr, 
  88.             .check_acl =generic_check_acl, 
  89.             #endif 
  90.             }; 
  91.         */  
  92.     error = dir->i_op->mknod(dir, dentry, mode, dev);//所以这里调用的就是shmem_mknod  
  93.     if (!error)  
  94.         fsnotify_create(dir, dentry);  
  95.     return error;  
  96. }  
  97.   
  98. shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)  
  99. {  
  100.     struct inode *inode;  
  101.     int error = -ENOSPC;  
  102.       
  103.     inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);//获得一个要创建的设备节点的inode,并初始化  
  104.     if (inode) {  
  105.         error = security_inode_init_security(inode, dir,&dentry->d_name,shmem_initxattrs, NULL);  
  106.         if (error) {  
  107.             if (error != -EOPNOTSUPP) {  
  108.                 iput(inode);  
  109.                 return error;  
  110.             }  
  111.         }  
  112. #ifdef CONFIG_TMPFS_POSIX_ACL  
  113.         error = generic_acl_init(inode, dir);  
  114.         if (error) {  
  115.             iput(inode);  
  116.             return error;  
  117.         }  
  118. #else  
  119.         error = 0;  
  120. #endif  
  121.         dir->i_size += BOGO_DIRENT_SIZE;  
  122.         dir->i_ctime = dir->i_mtime = CURRENT_TIME;  
  123.         d_instantiate(dentry, inode);//与dentry建立关,此时就可以在/dev下看到这个字符设备节点了  
  124.         dget(dentry); //递减dentry的计数  
  125.     }  
  126.     return error;  
  127. }  

文件系统的mount

  1. 内核主要是通过kernel_init调用prepare_namespace()函数执行安装实际根文件系统的操作:

[cpp] view plain copy
  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.     /* wait for 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. }   
  51.   
  52. int devtmpfs_mount(const char *mntdir)  
  53. {  
  54.     int err;  
  55.       
  56.     if (!mount_dev)  
  57.         return 0;  
  58.       
  59.     if (!thread)  
  60.         return 0;  
  61.     //将devtmpfs文件系统挂载到/dev目录下  
  62.     err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);  
  63.     if (err)  
  64.         printk(KERN_INFO "devtmpfs: error mounting %i\n", err);  
  65.     else  
  66.         printk(KERN_INFO "devtmpfs: mounted\n");  
  67.     return err;  
  68. }  

devtmpfs创建节点

  1. 系统在启动过程中,扫描到的设备会通过devtmpfs_create_node()函数来添加设备节点。
[cpp] view plain copy
  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. }  
  37.   
  38. const char *device_get_devnode(struct device *dev,umode_t *mode, const char **tmp)  
  39. {  
  40.     char *s;  
  41.       
  42.     *tmp = NULL;  
  43.       
  44.     /* the device type may provide a specific name */  
  45.     if (dev->type && dev->type->devnode)  
  46.         *tmp = dev->type->devnode(dev, mode);  
  47.     if (*tmp)  
  48.         return *tmp;  
  49.       
  50.     /* the class may provide a specific name */  
  51.     if (dev->class && dev->class->devnode)  
  52.         *tmp = dev->class->devnode(dev, mode);  
  53.     if (*tmp)  
  54.         return *tmp;  
  55.       
  56.     /* return name without allocation, tmp == NULL */  
  57.     if (strchr(dev_name(dev), '!') == NULL)  
  58.         return dev_name(dev);  
  59.       
  60.     /* replace '!' in the name with '/' */  
  61.     *tmp = kstrdup(dev_name(dev), GFP_KERNEL);  
  62.     if (!*tmp)  
  63.         return NULL;  
  64.     while ((s = strchr(*tmp, '!')))  
  65.         s[0] = '/';  
  66.     return *tmp;  
  67. }  

原文地址:  http://blog.chinaunix.net/uid-27717694-id-3574368.html