五、devtmpfs文件系统 创建设备节点

  1. 转载来自:http://blog.chinaunix.net/uid-27717694-id-3574368.html

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

  347. const char *device_get_devnode(struct device *dev,umode_t *mode, const char **tmp)
  348. {
  349.     char *s;
  350.     
  351.     *tmp = NULL;
  352.     
  353.     /* the device type may provide a specific name */
  354.     if (dev->type && dev->type->devnode)
  355.         *tmp = dev->type->devnode(dev, mode);
  356.     if (*tmp)
  357.         return *tmp;
  358.     
  359.     /* the class may provide a specific name */
  360.     if (dev->class && dev->class->devnode)
  361.         *tmp = dev->class->devnode(dev, mode);
  362.     if (*tmp)
  363.         return *tmp;
  364.     
  365.     /* return name without allocation, tmp == NULL */
  366.     if (strchr(dev_name(dev), '!') == NULL)
  367.         return dev_name(dev);
  368.     
  369.     /* replace '!' in the name with '/' */
  370.     *tmp = kstrdup(dev_name(dev), GFP_KERNEL);
  371.     if (!*tmp)
  372.         return NULL;
  373.     while ((= strchr(*tmp, '!')))
  374.         s[0] = '/';
  375.     return *tmp;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值