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;
- }
devtmpfs文件系统创建设备节点的更多相关文章
- Linux设备驱动实现自己主动创建设备节点
#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #inclu ...
- linux driver ------ 字符设备驱动 之 “ 创建设备节点流程 ”
在字符设备驱动开发的入门教程中,最常见的就是用device_create()函数来创建设备节点了,但是在之后阅读内核源码的过程中却很少见device_create()的踪影了,取而代之的是device ...
- linux驱动开发(四) 字符设备驱动框架(自动创建设备节点)
代码如下 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> # ...
- platform型设备在/dev目录下自动创建设备节点的分析【转】
转自:http://blog.csdn.net/rockrockwu/article/details/7357648 系统启动过程中platform设备.驱动注册完毕,为什么在/dev目录下就自动创建 ...
- linux驱动之设备号与创建设备节点
设备号: 1.自己主动分配 major = register_chrdev(0,"first_drv",&first_sdv_fops);//注冊 注冊设备时给设备号写0, ...
- 使用class 自动创建设备节点
#include <linux/init.h>// __init __exit #include <linux/module.h> // module_init module_ ...
- Linux 内核驱动自动创建设备节点并挂载设备
*注:本文来自http://blog.csdn.net/lwj103862095/article/details/17470573 一.首先需要在最开始定义两个数据结构: static struct ...
- Linux /dev 自动创建设备节点
#include <linux/module.h> #include <linux/module.h> #include <linux/kernel.h> #inc ...
- Android和Linux下设备节点的创建笔记
1. Linux kernel创建的/dev/下的设备节点是不对的, 其实是kernel仅负责在/sys/(基于内存的虚拟文件系统)创建一大堆下目录和文件,而真正的设备节点是在用户空间程序创建的,应该 ...
随机推荐
- JS this,call和apply以及回调函数
this this引用,引用的是一个对象,对象不同或函数调用方式的不同,this引用会根据代码的上下文语境自动改变引用对象的特性. 引用规则 1,在最外层代码中,this引用引用的是全局对象(wind ...
- MYSQL数据库如何赋予远程某个IP访问权限
1. 授权用户root使用密码jb51从任意主机连接到mysql服务器:代码如下:GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'jb ...
- EF+MySQL乐观锁控制电商并发下单扣减库存,在高并发下的问题
下订单减库存的方式 现在,连农村的大姐都会用手机上淘宝购物了,相信电商对大家已经非常熟悉了,如果熟悉电商开发的同学,就知道在买家下单购买商品的时候,是需要扣减库存的,当然有2种扣减库存的方式, 一种是 ...
- css知多少(1)——我来问你来答
1. 引言 各位前端或者伪前端(比如作者本人)的同志们,css对你们来说不是很陌生.比如我,在几年之前上大学的时候,给外面做网站就用css,而且必须用css.这样算下来也得六年多了,有些功能可能轻车熟 ...
- js改变css样式的三种方法
共用代码: <div id="div">this is a div</div> var div=document.getElementById('div') ...
- Restive.js – 轻松让网站变成响应式和自适应
Restive.js 是一个 jQuery 插件,可以帮助您轻松快捷地添加响应式功能到你网站,适应几乎所有拥有 Web 功能的设备.使用设备检测,高级管理断点,以及方向管理的组合,Restive.js ...
- 【HTML5】HTML5本地数据库(Web Sql Database)
Web Sql数据库简介 Web SQL数据库API实际上不是HTML5规范的组成部分,而是单独的规范.它通过一套API来操纵客户端的数据库. Web SQL数据库的浏览器支持情况 Web SQL 数 ...
- AngularJS结合RequireJS做文件合并压缩的那些坑
我在项目使用了AngularJS框架,用RequireJS做异步模块加载(AMD),在做文件合并压缩时,遇到了一些坑,有些只是解决了,但不明白原因. 那些坑 1. build.js里面的paths必须 ...
- html5音频和视频标签
在html5之前的版本中如果想要在网页中插入音频和视频必须要安装插件才可以,比如最常见的flash插件.很多人在刚安装一款浏览器的时候都会遇到浏览器建议安装flash插件,在移动端也是如此.如果想要在 ...
- 简单PHP会话(session)说明
现在程序员愈发的不容易了,想要精通,必然要寻本溯源,这其实与目前泛滥的愈发高级的语言以及众多的框架刚好相反,因为它们在尽可能的掩盖本源使其简单,个人称之为程序员学习悖论. 注:作者接触web开发和ph ...