devtmpfs文件系统创建设备节点
- 
分类: LINUX 原文地址:devtmpfs文件系统创建设备节点 作者:wangbaolin719 
- http://blog.chinaunix.net/uid-27097876-id-4334356.html
-  一、devtmpfs概述
 
- 
			1.devtmpfs 的功用是在 Linux 核心 启动早期建立一个初步的 /dev,令一般启动程序不用等待 udev,缩短 GNU/Linux 的开机时间。
 
- 
			
 
- 
			2.重要解释
 
- 
			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.
 
- 
			
 
- 
			3.menuconfig 中加入devtmpfs支持 
 
- 
			make menuconfig-->Device Drivers-->Generic Driver Options
 
- 
			Maintain a devtmpfs filesystem to mount at /dev
 
- 
			Automount devtmpfs at /dev, after the kernel mounted the rootfs
 
- 
			
 
- 
			4.df -T显示devtmpfs
 
- 
			文件系统    类型   1K-块      已用      可用 已用% 挂载点
 
- 
			/dev/sda1     ext4    31621016  14985712  15029008  50% /
 
- 
			none      devtmpfs      399552       276    399276   1% /dev
 
- 
			none         tmpfs      403804        24    403780   1% /dev/shm
 
- 
			none         tmpfs      403804       108    403696   1% /var/run
 
- 
			none         tmpfs      403804         0    403804   0% /var/lock
 
- 
			none         tmpfs      403804         0    403804   0% /lib/init/rw
 
- 
			.host:/ vmhgfs    67151668  54038400  13113268  81% /mnt/hgfs
 
- 
			/dev/loop0    ext2       16119      8528      6772  56% /mnt/loop
 
- 
			
 
- 
			二、devtmpfs文件系统初始化
 
- 
			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();
 
- 
			}
 
- 
			
 
- 
			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;
 
- 
			}
 
- 
			
 
- 
			//请求创建设备节点的请求队列req结构
 
- 
			static struct req {
 
- 
			    struct req *next;
 
- 
			    struct completion done;
 
- 
			    int err;
 
- 
			    const char *name;
 
- 
			    umode_t mode;//0代表删除
 
- 
			    struct device *dev;
 
- 
			} *requests;
 
- 
			
 
- 
			//内核线程devtmpfsd
 
- 
			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;
 
- 
			}
 
- 
			
 
- 
			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);
 
- 
			}
 
- 
			
 
- 
			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;
 
- 
			}
 
- 
			
 
- 
			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;
 
- 
			}
 
- 
			
 
- 
			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;
 
- 
			}
 
- 
			
 
- 
			三、文件系统的mount
 
- 
			内核主要是通过kernel_init调用prepare_namespace()函数执行安装实际根文件系统的操作:
 
- 
			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; 
 
- 
			  
 
- 
			    /* wait for 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("."); 
 
- 
			} 
 
- 
			
 
- 
			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;
 
- 
			}
 
- 
			
 
- 
			
 
- 
			四、devtmpfs创建节点
 
- 
			系统在启动过程中,扫描到的设备会通过devtmpfs_create_node()函数来添加设备节点。
 
- 
			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;
 
- 
			    
 
- 
			    /* the class 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;
 
- }
 
                    
                 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号