devtmpfs文件系统创建设备节点

摘要:
分类:LINUX原文地址:devtmpfs文件系统创建设备节点作者:wangbaolin719http://blog.chinaunix.net/uid-27097876-id-4334356.html一、devtmpfs概述1.devtmpfs的功用是在Linux核心启动早期建立一个初步的/dev,令一般启动程序不用等待udev,缩短GNU/Linux的开机时间。2.重要解释Devtmpfsle
  1. 分类: LINUX

    原文地址:devtmpfs文件系统创建设备节点 作者:wangbaolin719

  2. http://blog.chinaunix.net/uid-27097876-id-4334356.html
  3. 一、devtmpfs概述
  4. 1.devtmpfs 的功用是在 Linux 核心 启动早期建立一个初步的 /dev,令一般启动程序不用等待 udev,缩短 GNU/Linux 的开机时间。

  5. 2.重要解释
  6. 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.

  7. 3.menuconfig 中加入devtmpfs支持
  8. make menuconfig-->Device Drivers-->Generic Driver Options
  9. Maintain a devtmpfs filesystem to mount at /dev
  10. Automount devtmpfs at /dev, after the kernel mounted the rootfs

  11. 4.df -T显示devtmpfs
  12. 文件系统 类型 1K-块 已用 可用 已用% 挂载点
  13. /dev/sda1 ext4 31621016 14985712 15029008 50% /
  14. none devtmpfs 399552 276 399276 1% /dev
  15. none tmpfs 403804 24 403780 1% /dev/shm
  16. none tmpfs 403804 108 403696 1% /var/run
  17. none tmpfs 403804 0 403804 0% /var/lock
  18. none tmpfs 403804 0 403804 0% /lib/init/rw
  19. .host:/ vmhgfs 67151668 54038400 13113268 81% /mnt/hgfs
  20. /dev/loop0 ext2 16119 8528 6772 56% /mnt/loop

  21. 二、devtmpfs文件系统初始化
  22. void __init driver_init(void)
  23. {
  24. /* These are the core pieces */
  25. devtmpfs_init();//devtmpfs文件系统初始化
  26. devices_init();
  27. buses_init();
  28. classes_init();
  29. firmware_init();
  30. hypervisor_init();
  31. platform_bus_init();
  32. system_bus_init();
  33. cpu_dev_init();
  34. memory_dev_init();
  35. }

  36. static struct file_system_type dev_fs_type = {
  37. .name = "devtmpfs",
  38. .mount = dev_mount,
  39. .kill_sb = kill_litter_super,
  40. };

  41. int __init devtmpfs_init(void)
  42. {
  43. int err = register_filesystem(&dev_fs_type);//注册dev_fs_type文件系统,即将dev_fs_type添加到内核全局总链表中file_systems
  44. if (err) {
  45. printk(KERN_ERR "devtmpfs: unable to register devtmpfs ""type %i ", err);
  46. return err;
  47. }

  48. thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");//创建并启动一个内核线程devtmpfsd
  49. if (!IS_ERR(thread)) {
  50. wait_for_completion(&setup_done);//进行一个不可打断的等待,允许一个线程告诉另一个线程工作已经完成
  51. } else {
  52. err = PTR_ERR(thread);
  53. thread = NULL;
  54. }

  55. if (err) {
  56. printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i ", err);
  57. unregister_filesystem(&dev_fs_type);
  58. return err;
  59. }

  60. printk(KERN_INFO "devtmpfs: initialized ");
  61. return 0;
  62. }

  63. //请求创建设备节点的请求队列req结构
  64. static struct req {
  65. struct req *next;
  66. struct completion done;
  67. int err;
  68. const char *name;
  69. umode_t mode;//0代表删除
  70. struct device *dev;
  71. } *requests;

  72. //内核线程devtmpfsd
  73. static int devtmpfsd(void *p)
  74. {
  75. char options[] = "mode=0755";
  76. int *err = p;

  77. *err = sys_unshare(CLONE_NEWNS);
  78. if (*err)
  79. goto out;

  80. //挂载devtmpfs文件系统
  81. //devtmpfs是待安装设备的路径名,“/”是安装点路径名,”devtmpfs“表示文件系统类型,MS_SILENT=32768,即0x8000
  82. *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options);
  83. if (*err)
  84. goto out;
  85. sys_chdir("/.."); //将进程的当前工作目录(pwd)设定为devtmpfs文件系统的根目录/* will traverse into overmounted root */
  86. sys_chroot(".");
  87. complete(&setup_done);//允许一个线程告诉另一个线程工作已经完成
  88. while (1) {
  89. spin_lock(&req_lock);
  90. while (requests) {//扫描请求链表,每当要创建一个设备节点时,都需要向requests链表中添加请求
  91. struct req *req = requests;//赋值给临时req
  92. requests = NULL;//清空
  93. spin_unlock(&req_lock);
  94. while (req) {//遍历刚才requests的请求链表
  95. struct req *next = req->next;
  96. req->err = handle(req->name, req->mode, req->dev);//对链表中的每一个请求调用handle函数
  97. complete(&req->done);
  98. req = next;
  99. }
  100. spin_lock(&req_lock);
  101. }
  102. __set_current_state(TASK_INTERRUPTIBLE);//设置为睡眠状态
  103. spin_unlock(&req_lock);
  104. schedule();//系统切换
  105. }
  106. return 0;
  107. out:
  108. complete(&setup_done);
  109. return *err;
  110. }

  111. static int handle(const char *name, umode_t mode, struct device *dev)
  112. {
  113. if (mode)
  114. return handle_create(name, mode, dev);
  115. else
  116. return handle_remove(name, dev);
  117. }

  118. static int handle_create(const char *nodename, umode_t mode, struct device *dev)
  119. {
  120. struct dentry *dentry;
  121. struct path path;
  122. int err;

  123. //查找节点名称的路径以及返回节点对应的父目录dentry结构,即在此目录下创建一个设备节点,即是/dev目录对应的dentry结构
  124. dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
  125. if (dentry == ERR_PTR(-ENOENT)) {
  126. create_path(nodename);
  127. dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
  128. }
  129. if (IS_ERR(dentry))
  130. return PTR_ERR(dentry);

  131. //创建设备节点
  132. err = vfs_mknod(path.dentry->d_inode,dentry, mode, dev->devt);
  133. if (!err) {
  134. struct iattr newattrs;
  135. newattrs.ia_mode = mode;/* fixup possibly umasked mode */
  136. newattrs.ia_valid = ATTR_MODE;
  137. mutex_lock(&dentry->d_inode->i_mutex);
  138. notify_change(dentry, &newattrs);
  139. mutex_unlock(&dentry->d_inode->i_mutex);
  140. dentry->d_inode->i_private = &thread;/* mark as kernel-created inode */
  141. }
  142. done_path_create(&path, dentry);//与前边kern_path_create对应,减少path和dentry的计数等
  143. return err;
  144. }

  145. int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
  146. {
  147. int error = may_create(dir, dentry);//检查是否可以创建设备文件节点

  148. if (error)
  149. return error;

  150. //必须是字符设备或者块设备,且具有创建节点的权限
  151. if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
  152. return -EPERM;

  153. if (!dir->i_op->mknod)
  154. return -EPERM;

  155. error = devcgroup_inode_mknod(mode, dev);
  156. if (error)
  157. return error;

  158. error = security_inode_mknod(dir, dentry, mode, dev);
  159. if (error)
  160. return error;

  161. //调用具体文件系统的mknod()函数
  162. //mount时调用shmem_fill_super()-->shmem_get_inode()分配inode节点时做出的初始化
  163. /*那么在shmem_get_inode中
  164. caseS_IFDIR:
  165. inc_nlink(inode);
  166. inode->i_size= 2 * BOGO_DIRENT_SIZE;
  167. inode->i_op= &shmem_dir_inode_operations;
  168. inode->i_fop= &simple_dir_operations;
  169. 由于mountpoint是dev这个目录,所以dev对应的inode的i_op就是shmem_dir_inode_operations。
  170. staticconst struct inode_operations shmem_dir_inode_operations = {
  171. #ifdefCONFIG_TMPFS
  172. .create =shmem_create,
  173. .lookup =simple_lookup,
  174. .link =shmem_link,
  175. .unlink =shmem_unlink,
  176. .symlink =shmem_symlink,
  177. .mkdir =shmem_mkdir,
  178. .rmdir =shmem_rmdir,
  179. .mknod =shmem_mknod,
  180. .rename =shmem_rename,
  181. #endif
  182. #ifdefCONFIG_TMPFS_POSIX_ACL
  183. .setattr =shmem_notify_change,
  184. .setxattr =generic_setxattr,
  185. .getxattr =generic_getxattr,
  186. .listxattr =generic_listxattr,
  187. .removexattr =generic_removexattr,
  188. .check_acl =generic_check_acl,
  189. #endif
  190. };
  191. */
  192. error = dir->i_op->mknod(dir, dentry, mode, dev);//所以这里调用的就是shmem_mknod
  193. if (!error)
  194. fsnotify_create(dir, dentry);
  195. return error;
  196. }

  197. shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
  198. {
  199. struct inode *inode;
  200. int error = -ENOSPC;

  201. inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);//获得一个要创建的设备节点的inode,并初始化
  202. if (inode) {
  203. error = security_inode_init_security(inode, dir,&dentry->d_name,shmem_initxattrs, NULL);
  204. if (error) {
  205. if (error != -EOPNOTSUPP) {
  206. iput(inode);
  207. return error;
  208. }
  209. }
  210. #ifdef CONFIG_TMPFS_POSIX_ACL
  211. error = generic_acl_init(inode, dir);
  212. if (error) {
  213. iput(inode);
  214. return error;
  215. }
  216. #else
  217. error = 0;
  218. #endif
  219. dir->i_size += BOGO_DIRENT_SIZE;
  220. dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  221. d_instantiate(dentry, inode);//与dentry建立关,此时就可以在/dev下看到这个字符设备节点了
  222. dget(dentry); //递减dentry的计数
  223. }
  224. return error;
  225. }

  226. 三、文件系统的mount
  227. 内核主要是通过kernel_init调用prepare_namespace()函数执行安装实际根文件系统的操作:
  228. void __init prepare_namespace(void)
  229. {
  230. int is_floppy;

  231. if (root_delay) {
  232. printk(KERN_INFO "Waiting %dsec before mounting root device... ",
  233. root_delay);
  234. ssleep(root_delay);
  235. }
  236. wait_for_device_probe();

  237. md_run_setup();
  238. /* 把root_device_name变量置为从启动参数“root”中获取的设备文件名。
  239. * 同样,把ROOT_DEV变量置为同一设备文件的主设备号和次设备号。*/
  240. if (saved_root_name[0]) {
  241. root_device_name = saved_root_name;
  242. if (!strncmp(root_device_name, "mtd", 3) ||
  243. !strncmp(root_device_name, "ubi", 3)) {
  244. mount_block_root(root_device_name, root_mountflags);
  245. goto out;
  246. }
  247. ROOT_DEV = name_to_dev_t(root_device_name);//转换为设备号/dev/mtdblock2.
  248. if (strncmp(root_device_name, "/dev/", 5) == 0)
  249. root_device_name += 5;
  250. }

  251. if (initrd_load())
  252. goto out;

  253. /* wait for any asynchronous scanning to complete */
  254. if ((ROOT_DEV == 0) && root_wait) {
  255. printk(KERN_INFO "Waiting for root device %s... ",
  256. saved_root_name);
  257. while (driver_probe_done() != 0 ||
  258. (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
  259. msleep(100);
  260. async_synchronize_full();
  261. }

  262. is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

  263. if (is_floppy && rd_doload && rd_load_disk(0))
  264. ROOT_DEV = Root_RAM0;

  265. mount_root();
  266. out:
  267. devtmpfs_mount("dev");//挂载devtmpfs文件系统
  268. sys_mount(".", "/", NULL, MS_MOVE, NULL); /* 移动rootfs文件系统根目录上的已安装文件系统的安装点。 */
  269. sys_chroot(".");
  270. }

  271. int devtmpfs_mount(const char *mntdir)
  272. {
  273. int err;

  274. if (!mount_dev)
  275. return 0;

  276. if (!thread)
  277. return 0;
  278. //将devtmpfs文件系统挂载到/dev目录下
  279. err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
  280. if (err)
  281. printk(KERN_INFO "devtmpfs: error mounting %i ", err);
  282. else
  283. printk(KERN_INFO "devtmpfs: mounted ");
  284. return err;
  285. }


  286. 四、devtmpfs创建节点
  287. 系统在启动过程中,扫描到的设备会通过devtmpfs_create_node()函数来添加设备节点。
  288. int devtmpfs_create_node(struct device *dev)
  289. {
  290. const char *tmp = NULL;
  291. struct req req;

  292. if (!thread)
  293. return 0;

  294. req.mode = 0;
  295. req.name = device_get_devnode(dev, &req.mode, &tmp);//获得设备名
  296. if (!req.name)
  297. return -ENOMEM;

  298. if (req.mode == 0)
  299. req.mode = 0600;
  300. if (is_blockdev(dev))
  301. req.mode |= S_IFBLK;//块设备
  302. else
  303. req.mode |= S_IFCHR;//字符设备

  304. req.dev = dev;

  305. init_completion(&req.done);

  306. spin_lock(&req_lock);
  307. req.next = requests;//请求添加到requests链表
  308. requests = &req;
  309. spin_unlock(&req_lock);

  310. wake_up_process(thread);//唤醒内核线程devtmpfsd添加设备节点
  311. wait_for_completion(&req.done);

  312. kfree(tmp);

  313. return req.err;
  314. }

  315. const char *device_get_devnode(struct device *dev,umode_t *mode, const char **tmp)
  316. {
  317. char *s;

  318. *tmp = NULL;

  319. /* the device type may provide a specific name */
  320. if (dev->type && dev->type->devnode)
  321. *tmp = dev->type->devnode(dev, mode);
  322. if (*tmp)
  323. return *tmp;

  324. /* the class may provide a specific name */
  325. if (dev->class && dev->class->devnode)
  326. *tmp = dev->class->devnode(dev, mode);
  327. if (*tmp)
  328. return *tmp;

  329. /* return name without allocation, tmp == NULL */
  330. if (strchr(dev_name(dev), '!') == NULL)
  331. return dev_name(dev);

  332. /* replace '!' in the name with '/' */
  333. *tmp = kstrdup(dev_name(dev), GFP_KERNEL);
  334. if (!*tmp)
  335. return NULL;
  336. while ((s = strchr(*tmp, '!')))
  337. s[0] = '/';
  338. return *tmp;
  339. }

免责声明:文章转载自《devtmpfs文件系统创建设备节点》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇oschina(开源中国)的Git代码托管平台使用教程Unity 如何在打包的时候执行一些逻辑下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

Lynx浏览器简明使用指南(转)

Lynx可以运行在很多种操作系统下,如VMS,UNIX,Windows95,WindowsNT等,当然也包括Linux。由于没有漂亮的图形界面,所以Lynx占用资源极少,而且速度很快。另外Lynx还是唯一能在字符终端下运行的WWW浏览器。Lynx的主页地址是:http://lynx.browser.org,另外http://www.cc.ukans.edu/...

c# Socket心跳试验,自定义发送包 和 使用KeepAlive

我记录了我心跳的位置,但WireShark无法检测到正在发送的消息,主要是因为发送的数据大小为0。如果网络电缆被拔掉,下次检测到心跳时就会报告错误。虽然这种方法可以检测套接字是否断开,但它不是很好,响应也不及时。当使用KeepAlive时,WireShark通常会检测到它不停地向Socket服务器发送消息,即心跳检测。图:通过三次握手(前三次握手)建立连接后...

CUPS

杯子:一个。工具1.hal设备管理器2.系统配置打印机3.Web管理器/etc/cups/ccups。conf/etc/cups/printer conf II。打印机本地安装和客户端安装1.在本地安装Linux打印机时,应选择postscript和pcl打印机。如果没有,则应将打印机设置为原始打印模式/etc/cups/printers。有限公司...

java 服务接口API限流 Rate Limit

服务接口的流量控制策略:分流、降级、流量限制等。2)使用Reids的列表结构,而不是incr命令1FUNCTIONLIMIT_API_CALLL2current=LLEN3IFcurrent˃10THEN4ERROR“toomanyrequestsperssecond”5ELSE6IFEXIST==FALSE7MULTI8RPUSH9EXPIRE10EXEC...

nginx 浏览php的时候会变成下载

php的时候会变成下载:这是因为nginx没有设置好碰到php文件时,要传递到后方的php解释器。当然啦,你的php-fpm解析器也需要正常运行,并监听好9000端口,才能最终生效并有效处理php脚本。windows下开启监听的办法,php-cgi.exe-b127.0.0.1:9000-cphpphp.ini待续:。。。。。...

easyExcel自动合并单元格

importcom.alibaba.excel.write.handler.CellWriteHandler;importorg.apache.poi.ss.usermodel.Sheet;importorg.apache.poi.ss.util.CellRangeAddress;int[]mergeColumnIndex){this.mergeRowInd...