devtmpfs文件系统创建设备节点
- 一、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;
-
}
-
}
-
-
error = generic_acl_init(inode, dir);
-
if (error){
-
iput(inode);
-
return error;
-
}
-
-
error = 0;
-
-
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;
-
}
-
-
posted on 2019-08-15 14:44 guanxi0808 阅读(1219) 评论(0) 收藏 举报
浙公网安备 33010602011771号