1. 程式人生 > >五、devtmpfs檔案系統 建立裝置節點

五、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. int __init devtmpfs_init(void)
  41. {
  42.     int err = register_filesystem(&dev_fs_type);//註冊dev_fs_type檔案系統,即將dev_fs_type新增到核心全域性總連結串列中file_systems
  43.     if (err) {
  44.         printk(KERN_ERR "devtmpfs: unable to register devtmpfs ""type %i\n", err);
  45.         return err;
  46.     }
  47.     thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");//建立並啟動一個核心執行緒devtmpfsd
  48.     if (!IS_ERR(thread)) {
  49.         wait_for_completion(&setup_done);//進行一個不可打斷的等待,允許一個執行緒告訴另一個執行緒工作已經完成
  50.     } else {
  51.         err = PTR_ERR(thread);
  52.         thread = NULL;
  53.     }
  54.     if (err) {
  55.         printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
  56.         unregister_filesystem(&dev_fs_type);
  57.         return err;
  58.     }
  59.     printk(KERN_INFO "devtmpfs: initialized\n");
  60.     return 0;
  61. }
  62. //請求建立裝置節點的請求佇列req結構
  63. static struct req {
  64.     struct req *next;
  65.     struct completion done;
  66.     int err;
  67.     const char *name;
  68.     umode_t mode;//0代表刪除
  69.     struct device *dev;
  70. } *requests;
  71. //核心執行緒devtmpfsd
  72. static int devtmpfsd(void *p)
  73. {
  74.     char options[] = "mode=0755";
  75.     int *err = p;
  76.     *err = sys_unshare(CLONE_NEWNS);
  77.     if (*err)
  78.         goto out;
  79.     //掛載devtmpfs檔案系統
  80.     //devtmpfs是待安裝裝置的路徑名,“/”是安裝點路徑名,”devtmpfs“表示檔案系統型別,MS_SILENT=32768,即0x8000
  81.     *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options);
  82.     if (*err)
  83.         goto out;
  84.     sys_chdir("/.."); //將程序的當前工作目錄(pwd)設定為devtmpfs檔案系統的根目錄/* will traverse into overmounted root */
  85.     sys_chroot(".");
  86.     complete(&setup_done);//允許一個執行緒告訴另一個執行緒工作已經完成
  87.     while (1) {
  88.         spin_lock(&req_lock);
  89.         while (requests) {//掃描請求連結串列,每當要建立一個裝置節點時,都需要向requests連結串列中新增請求
  90.             struct req *req = requests;//賦值給臨時req
  91.             requests = NULL;//清空
  92.             spin_unlock(&req_lock);
  93.             while (req) {//遍歷剛才requests的請求連結串列
  94.                 struct req *next = req->next;
  95.                 req->err = handle(req->name, req->mode, req->dev);//對連結串列中的每一個請求呼叫handle函式
  96.                 complete(&req->done);
  97.                 req = next;
  98.             }
  99.             spin_lock(&req_lock);
  100.         }
  101.         __set_current_state(TASK_INTERRUPTIBLE);//設定為睡眠狀態
  102.         spin_unlock(&req_lock);
  103.         schedule();//系統切換
  104.     }
  105.     return 0;
  106. out:
  107.     complete(&setup_done);
  108.     return *err;
  109. }
  110. static int handle(const char *name, umode_t mode, struct device *dev)
  111. {
  112.     if (mode)
  113.         return handle_create(name, mode, dev);
  114.     else
  115.         return handle_remove(name, dev);
  116. }
  117. static int handle_create(const char *nodename, umode_t mode, struct device *dev)
  118. {
  119.     struct dentry *dentry;
  120.     struct path path;
  121.     int err;
  122.     //查詢節點名稱的路徑以及返回節點對應的父目錄dentry結構,即在此目錄下建立一個裝置節點,即是/dev目錄對應的dentry結構
  123.     dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
  124.     if (dentry == ERR_PTR(-ENOENT)) {
  125.         create_path(nodename);
  126.         dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
  127.     }
  128.     if (IS_ERR(dentry))
  129.         return PTR_ERR(dentry);
  130.     //建立裝置節點
  131.     err = vfs_mknod(path.dentry->d_inode,dentry, mode, dev->devt);
  132.     if (!err) {
  133.         struct iattr newattrs;
  134.         newattrs.ia_mode = mode;/* fixup possibly umasked mode */
  135.         newattrs.ia_valid = ATTR_MODE;
  136.         mutex_lock(&dentry->d_inode->i_mutex);
  137.         notify_change(dentry, &newattrs);
  138.         mutex_unlock(&dentry->d_inode->i_mutex);
  139.         dentry->d_inode->i_private = &thread;/* mark as kernel-created inode */
  140.     }
  141.     done_path_create(&path, dentry);//與前邊kern_path_create對應,減少path和dentry的計數等
  142.     return err;
  143. }
  144. int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
  145. {
  146.     int error = may_create(dir, dentry);//檢查是否可以建立裝置檔案節點
  147.     if (error)
  148.         return error;
  149.     //必須是字元裝置或者塊裝置,且具有建立節點的許可權
  150.     if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
  151.         return -EPERM;
  152.     if (!dir->i_op->mknod)
  153.         return -EPERM;
  154.     error = devcgroup_inode_mknod(mode, dev);
  155.     if (error)
  156.         return error;
  157.     error = security_inode_mknod(dir, dentry, mode, dev);
  158.     if (error)
  159.         return error;
  160.     //呼叫具體檔案系統的mknod()函式
  161.     //mount時呼叫shmem_fill_super()-->shmem_get_inode()分配inode節點時做出的初始化
  162.     /*那麼在shmem_get_inode中
  163.         caseS_IFDIR:
  164.         inc_nlink(inode);
  165.         inode->i_size= 2 * BOGO_DIRENT_SIZE;
  166.         inode->i_op= &shmem_dir_inode_operations;
  167.         inode->i_fop= &simple_dir_operations;
  168.         由於mountpoint是dev這個目錄,所以dev對應的inode的i_op就是shmem_dir_inode_operations。
  169.         staticconst struct inode_operations shmem_dir_inode_operations = {
  170.             #ifdefCONFIG_TMPFS
  171.             .create =shmem_create,
  172.             .lookup =simple_lookup,
  173.             .link =shmem_link,
  174.             .unlink =shmem_unlink,
  175.             .symlink =shmem_symlink,
  176.             .mkdir =shmem_mkdir,
  177.             .rmdir =shmem_rmdir,
  178.             .mknod =shmem_mknod,
  179.             .rename =shmem_rename,
  180.             #endif
  181.             #ifdefCONFIG_TMPFS_POSIX_ACL
  182.             .setattr =shmem_notify_change,
  183.             .setxattr =generic_setxattr,
  184.             .getxattr =generic_getxattr,
  185.             .listxattr =generic_listxattr,
  186.             .removexattr =generic_removexattr,
  187.             .check_acl =generic_check_acl,
  188.             #endif
  189.             };
  190.         */
  191.     error = dir->i_op->mknod(dir, dentry, mode, dev);//所以這裡呼叫的就是shmem_mknod
  192.     if (!error)
  193.         fsnotify_create(dir, dentry);
  194.     return error;
  195. }
  196. shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
  197. {
  198.     struct inode *inode;
  199.     int error = -ENOSPC;
  200.     inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);//獲得一個要建立的裝置節點的inode,並初始化
  201.     if (inode) {
  202.         error = security_inode_init_security(inode, dir,&dentry->d_name,shmem_initxattrs, NULL);
  203.         if (error) {
  204.             if (error != -EOPNOTSUPP) {
  205.                 iput(inode);
  206.                 return error;
  207.             }
  208.         }
  209. #ifdef CONFIG_TMPFS_POSIX_ACL
  210.         error = generic_acl_init(inode, dir);
  211.         if (error) {
  212.             iput(inode);
  213.             return error;
  214.         }
  215. #else
  216.         error = 0;
  217. #endif
  218.         dir->i_size += BOGO_DIRENT_SIZE;
  219.         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  220.         d_instantiate(dentry, inode);//與dentry建立關,此時就可以在/dev下看到這個字元裝置節點了
  221.         dget(dentry); //遞減dentry的計數
  222.     }
  223.     return error;
  224. }
  225. 三、檔案系統的mount
  226. 核心主要是通過kernel_init呼叫prepare_namespace()函式執行安裝實際根檔案系統的操作
  227. void __init prepare_namespace(void) 
  228. { 
  229.     int is_floppy; 
  230.     if (root_delay) { 
  231.         printk(KERN_INFO "Waiting %dsec before mounting root device...\n", 
  232.                root_delay); 
  233.         ssleep(root_delay); 
  234.     } 
  235.     wait_for_device_probe(); 
  236.     md_run_setup(); 
  237.     /* 把root_device_name變數置為從啟動引數“root”中獲取的裝置檔名。 
  238.   * 同樣,把ROOT_DEV變數置為同一裝置檔案的主裝置號和次裝置號。*/ 
  239.     if (saved_root_name[0]) { 
  240.         root_device_name = saved_root_name; 
  241.         if (!strncmp(root_device_name, "mtd", 3) || 
  242.             !strncmp(root_device_name, "ubi", 3)) { 
  243.             mount_block_root(root_device_name, root_mountflags); 
  244.             goto out; 
  245.         } 
  246.         ROOT_DEV = name_to_dev_t(root_device_name);//轉換為裝置號/dev/mtdblock2. 
  247.         if (strncmp(root_device_name, "/dev/", 5) == 0) 
  248.             root_device_name += 5; 
  249.     } 
  250.     if (initrd_load()) 
  251.         goto out; 
  252.     /* wait for any asynchronous scanning to complete */ 
  253.     if ((ROOT_DEV == 0) && root_wait) { 
  254.         printk(KERN_INFO "Waiting for root device %s...\n", 
  255.             saved_root_name); 
  256.         while (driver_probe_done() != 0 || 
  257.             (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0) 
  258.             msleep(100); 
  259.         async_synchronize_full(); 
  260.     } 
  261.     is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; 
  262.     if (is_floppy && rd_doload && rd_load_disk(0)) 
  263.         ROOT_DEV = Root_RAM0; 
  264.     mount_root(); 
  265. out: 
  266.     devtmpfs_mount("dev");//掛載devtmpfs檔案系統 
  267.     sys_mount(".", "/", NULL, MS_MOVE, NULL); /* 移動rootfs檔案系統根目錄上的已安裝檔案系統的安裝點。 */ 
  268.     sys_chroot("."); 
  269. } 
  270. int devtmpfs_mount(const char *mntdir)
  271. {
  272.     int err;
  273.     if (!mount_dev)
  274.         return 0;
  275.     if (!thread)
  276.         return 0;
  277.     //將devtmpfs檔案系統掛載到/dev目錄下
  278.     err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
  279.     if (err)
  280.         printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
  281.     else
  282.         printk(KERN_INFO "devtmpfs: mounted\n");
  283.     return err;
  284. }
  285. 四、devtmpfs建立節點
  286. 系統在啟動過程中,掃描到的裝置會通過devtmpfs_create_node()函式來新增裝置節點
  287. int devtmpfs_create_node(struct device *dev)
  288. {
  289.     const char *tmp = NULL;
  290.     struct req req;
  291.     if (!thread)
  292.     return 0;
  293.     req.mode = 0;
  294.     req.name = device_get_devnode(dev, &req.mode, &tmp);//獲得裝置名
  295.     if (!req.name)
  296.         return -ENOMEM;
  297.     if (req.mode == 0)
  298.         req.mode = 0600;
  299.     if (is_blockdev(dev))
  300.         req.mode |= S_IFBLK;//塊裝置
  301.     else
  302.         req.mode |= S_IFCHR;//字元裝置
  303.     req.dev = dev;
  304.     init_completion(&req.done);
  305.     spin_lock(&req_lock);
  306.     req.next = requests;//請求新增到requests連結串列
  307.     requests = &req;
  308.     spin_unlock(&req_lock);
  309.     wake_up_process(thread);//喚醒核心執行緒devtmpfsd新增裝置節點
  310.     wait_for_completion(&req.done);
  311.     kfree(tmp);
  312.     return req.err;
  313. }
  314. const char *device_get_devnode(struct device *dev,umode_t *mode, const char **tmp)
  315. {
  316.     char *s;
  317.     *tmp = NULL;
  318.     /* the device type may provide a specific name */
  319.     if (dev->type && dev->type->devnode)
  320.         *tmp = dev->type->devnode(dev, mode);
  321.     if (*tmp)
  322.         return *tmp;
  323.     /* the class may provide a specific name */
  324.     if (dev->class && dev->class->devnode)
  325.         *tmp = dev->class->devnode(dev, mode);
  326.     if (*tmp)
  327.         return *tmp;
  328.     /* return name without allocation, tmp == NULL */
  329.     if (strchr(dev_name(dev), '!') == NULL)
  330.         return dev_name(dev);
  331.     /* replace '!' in the name with '/' */
  332.     *tmp = kstrdup(dev_name(dev), GFP_KERNEL);
  333.     if (!*tmp)
  334.         return NULL;
  335.     while ((= strchr(*tmp, '!')))
  336.         s[0] = '/';
  337.     return *tmp;