- 一、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 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/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; /* 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("."); }
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; /* 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; }
devtmpfs文件系统创建设备节点
最新推荐文章于 2024-06-19 20:58:40 发布