在文件读写之前,我们必须先打开文件。从应用程序的角度来看,这是通过标准库的open函数完成的,该函数返回一个文件描述符。内核中是由系统调用sys_open()函数完成。

[cpp] view plain copy  print?
  1. /*sys_open*/
  2. SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
  3. {
  4. long ret;
  5. /*检查是否应该不考虑用户层传递的标志、总是强行设置
  6. O_LARGEFILE标志。如果底层处理器的字长不是32位,就是这种
  7. 情况*/
  8. if (force_o_largefile())
  9. flags |= O_LARGEFILE;
  10. /*实际工作*/
  11. ret = do_sys_open(AT_FDCWD, filename, flags, mode);
  12. /* avoid REGPARM breakage on x86: */
  13. asmlinkage_protect(3, ret, filename, flags, mode);
  14. return ret;
  15. }

实际实现工作

[cpp] view plain copy  print?
  1. <pre class="cpp" name="code">long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
  2. {
  3. /*从进程地址空间读取该文件的路径名*/
  4. char *tmp = getname(filename);
  5. int fd = PTR_ERR(tmp);
  6. if (!IS_ERR(tmp)) {
  7. /*在内核中,每个打开的文件由一个文件描述符表示
  8. 该描述符在特定于进程的数组中充当位置索引(数组是
  9. task_struct->files->fd_arry),该数组的元素包含了file结构,其中
  10. 包括每个打开文件的所有必要信息。因此,调用下面
  11. 函数查找一个未使用的文件描述符,返回的是上面
  12. 说的数组的下标*/
  13. fd = get_unused_fd_flags(flags);
  14. if (fd >= 0) {
  15. /*fd获取成功则开始打开文件,此函数是主要完成打开功能的函数*/
  16. struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
  17. if (IS_ERR(f)) {
  18. put_unused_fd(fd);
  19. fd = PTR_ERR(f);
  20. } else {
  21. fsnotify_open(f->f_path.dentry);
  22. fd_install(fd, f);
  23. }
  24. }
  25. putname(tmp);
  26. }
  27. return fd;
  28. }</pre><br>
  29. <pre></pre>
  30. <p><span style="font-size:18px">打开文件主体实现</span></p>
  31. <pre class="cpp" name="code">/*
  32. * Note that the low bits of the passed in "open_flag"
  33. * are not the same as in the local variable "flag". See
  34. * open_to_namei_flags() for more details.
  35. */
  36. struct file *do_filp_open(int dfd, const char *pathname,
  37. int open_flag, int mode, int acc_mode)
  38. {
  39. struct file *filp;
  40. struct nameidata nd;
  41. int error;
  42. struct path path;
  43. struct dentry *dir;
  44. int count = 0;
  45. int will_write;
  46. /*改变参数flag的值,具体做法是flag+1*/
  47. int flag = open_to_namei_flags(open_flag);
  48. /*设置访问权限*/
  49. if (!acc_mode)
  50. acc_mode = MAY_OPEN | ACC_MODE(flag);
  51. /* O_TRUNC implies we need access checks for write permissions */
  52. /*根据 O_TRUNC标志设置写权限 */
  53. if (flag & O_TRUNC)
  54. acc_mode |= MAY_WRITE;
  55. /* Allow the LSM permission hook to distinguish append
  56. access from general write access. */
  57. /* 设置O_APPEND 标志*/
  58. if (flag & O_APPEND)
  59. acc_mode |= MAY_APPEND;
  60. /*
  61. * The simplest case - just a plain lookup.
  62. */
  63. /*如果不是创建文件*/
  64. if (!(flag & O_CREAT)) {
  65. /*当内核要访问一个文件的时候,第一步要做的是找到这个文件,
  66. 而查找文件的过程在vfs里面是由path_lookup或者path_lookup_open函数来完成的。
  67. 这两个函数将用户传进来的字符串表示的文件路径转换成一个dentry结构,
  68. 并建立好相应的inode和file结构,将指向file的描述符返回用户。用户随后
  69. 通过文件描述符,来访问这些数据结构*/
  70. error = path_lookup_open(dfd, pathname, lookup_flags(flag),
  71. &nd, flag);
  72. if (error)
  73. return ERR_PTR(error);
  74. goto ok;/*跳过下面的创建部分*/
  75. }
  76. /*
  77. * Create - we need to know the parent.
  78. */
  79. /*到此则是要创建文件*/
  80. /* path-init为查找作准备工作,path_walk真正上路查找,
  81. 这两个函数联合起来根据一段路径名找到对应的dentry */
  82. error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
  83. if (error)
  84. return ERR_PTR(error);
  85. error = path_walk(pathname, &nd);
  86. if (error) {
  87. if (nd.root.mnt)
  88. path_put(&nd.root);
  89. return ERR_PTR(error);
  90. }
  91. if (unlikely(!audit_dummy_context()))
  92. /*保存inode节点信息*/
  93. audit_inode(pathname, nd.path.dentry);
  94. /*
  95. * We have the parent and last component. First of all, check
  96. * that we are not asked to creat(2) an obvious directory - that
  97. * will not do.
  98. */
  99. error = -EISDIR;
  100. /*父节点信息*/
  101. if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])
  102. goto exit_parent;
  103. error = -ENFILE;
  104. /*获取文件指针*/
  105. filp = get_empty_filp();
  106. if (filp == NULL)
  107. goto exit_parent;
  108. /*填充nameidata 结构*/
  109. nd.intent.open.file = filp;
  110. nd.intent.open.flags = flag;
  111. nd.intent.open.create_mode = mode;
  112. dir = nd.path.dentry;
  113. nd.flags &= ~LOOKUP_PARENT;
  114. nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;
  115. if (flag & O_EXCL)
  116. nd.flags |= LOOKUP_EXCL;
  117. mutex_lock(&dir->d_inode->i_mutex);
  118. /*从哈希表中查找目的文件对应的dentry,上面路径搜索的是父节点
  119. 也就是目的文件的上一层目录,为了得到目的文件的
  120. path结构,我们用nd中的last结构和上一层目录的dentry结构
  121. 可以找到*/
  122. path.dentry = lookup_hash(&nd);
  123. path.mnt = nd.path.mnt;
  124. /*到此目标节点的path结构已经找到*/
  125. do_last:
  126. error = PTR_ERR(path.dentry);
  127. if (IS_ERR(path.dentry)) {
  128. mutex_unlock(&dir->d_inode->i_mutex);
  129. goto exit;
  130. }
  131. if (IS_ERR(nd.intent.open.file)) {
  132. error = PTR_ERR(nd.intent.open.file);
  133. goto exit_mutex_unlock;
  134. }
  135. /* Negative dentry, just create the file */
  136. /*如果此dentry结构没有对应的inode节点,说明是无效的,应该创建文件节点 */
  137. if (!path.dentry->d_inode) {
  138. /*
  139. * This write is needed to ensure that a
  140. * ro->rw transition does not occur between
  141. * the time when the file is created and when
  142. * a permanent write count is taken through
  143. * the 'struct file' in nameidata_to_filp().
  144. */
  145. /*write权限是必需的*/
  146. error = mnt_want_write(nd.path.mnt);
  147. if (error)
  148. goto exit_mutex_unlock;
  149. /*按照namei格式的flag open*,主要是创建inode*/
  150. error = __open_namei_create(&nd, &path, flag, mode);
  151. if (error) {
  152. mnt_drop_write(nd.path.mnt);
  153. goto exit;
  154. }
  155. /*根据nameidata 得到相应的file结构*/
  156. filp = nameidata_to_filp(&nd, open_flag);
  157. if (IS_ERR(filp))
  158. ima_counts_put(&nd.path,
  159. acc_mode & (MAY_READ | MAY_WRITE |
  160. MAY_EXEC));
  161. /*放弃写权限*/
  162. mnt_drop_write(nd.path.mnt);
  163. if (nd.root.mnt)
  164. path_put(&nd.root);
  165. return filp;
  166. }
  167. /*
  168. * It already exists.
  169. */
  170. /*要打开的文件已经存在*/
  171. mutex_unlock(&dir->d_inode->i_mutex);
  172. /*保存inode节点*/
  173. audit_inode(pathname, path.dentry);
  174. error = -EEXIST;
  175. if (flag & O_EXCL)
  176. goto exit_dput;
  177. /*如果path上安装了文件系统,则依次往下找,直到找到
  178. 的文件系统没有安装别的文件系统,更新path结构为
  179. 此文件系统的根目录信息*/
  180. if (__follow_mount(&path)) {
  181. error = -ELOOP;
  182. if (flag & O_NOFOLLOW)
  183. goto exit_dput;
  184. }
  185. error = -ENOENT;
  186. if (!path.dentry->d_inode)
  187. goto exit_dput;
  188. if (path.dentry->d_inode->i_op->follow_link)
  189. goto do_link;/*顺次遍历符号链接*/
  190. /*路径转化为相应的nameidata 结构*/
  191. path_to_nameidata(&path, &nd);
  192. error = -EISDIR;
  193. /*如果是文件夹*/
  194. if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
  195. goto exit;
  196. /*到这里,nd结构中存放的信息已经是最后的目的文件信息*/
  197. ok:
  198. /*
  199. * Consider:
  200. * 1. may_open() truncates a file
  201. * 2. a rw->ro mount transition occurs
  202. * 3. nameidata_to_filp() fails due to
  203. *    the ro mount.
  204. * That would be inconsistent, and should
  205. * be avoided. Taking this mnt write here
  206. * ensures that (2) can not occur.
  207. */
  208. will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);
  209. if (will_write) {
  210. error = mnt_want_write(nd.path.mnt);
  211. if (error)
  212. goto exit;
  213. }
  214. /*may_open执行权限检测、文件打开和truncate的操作*/
  215. error = may_open(&nd.path, acc_mode, flag);
  216. if (error) {
  217. if (will_write)
  218. mnt_drop_write(nd.path.mnt);
  219. goto exit;
  220. }
  221. /*将nameidata转化为file*/
  222. filp = nameidata_to_filp(&nd, open_flag);
  223. if (IS_ERR(filp))
  224. ima_counts_put(&nd.path,
  225. acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
  226. /*
  227. * It is now safe to drop the mnt write
  228. * because the filp has had a write taken
  229. * on its behalf.
  230. */
  231. if (will_write)
  232. /*释放写权限*/
  233. mnt_drop_write(nd.path.mnt);
  234. if (nd.root.mnt)
  235. /*释放引用计数*/
  236. path_put(&nd.root);
  237. return filp;
  238. exit_mutex_unlock:
  239. mutex_unlock(&dir->d_inode->i_mutex);
  240. exit_dput:
  241. path_put_conditional(&path, &nd);
  242. exit:
  243. if (!IS_ERR(nd.intent.open.file))
  244. release_open_intent(&nd);
  245. exit_parent:
  246. if (nd.root.mnt)
  247. path_put(&nd.root);
  248. path_put(&nd.path);
  249. return ERR_PTR(error);
  250. /*允许遍历连接文件,则手工找到连接文件对应的文件*/
  251. do_link:
  252. error = -ELOOP;
  253. if (flag & O_NOFOLLOW)
  254. goto exit_dput;/*不允许遍历连接文件,返回错误*/
  255. /*
  256. * This is subtle. Instead of calling do_follow_link() we do the
  257. * thing by hands. The reason is that this way we have zero link_count
  258. * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
  259. * After that we have the parent and last component, i.e.
  260. * we are in the same situation as after the first path_walk().
  261. * Well, almost - if the last component is normal we get its copy
  262. * stored in nd->last.name and we will have to putname() it when we
  263. * are done. Procfs-like symlinks just set LAST_BIND.
  264. */
  265. /*以下是手工找到链接文件对应的文件dentry结构代码
  266. */
  267. /*设置查找LOOKUP_PARENT标志*/
  268. nd.flags |= LOOKUP_PARENT;
  269. /*判断操作是否安全*/
  270. error = security_inode_follow_link(path.dentry, &nd);
  271. if (error)
  272. goto exit_dput;
  273. /*处理符号链接,即路径搜索,结果放入nd中*/
  274. error = __do_follow_link(&path, &nd);
  275. if (error) {
  276. /* Does someone understand code flow here? Or it is only
  277. * me so stupid? Anathema to whoever designed this non-sense
  278. * with "intent.open".
  279. */
  280. release_open_intent(&nd);
  281. if (nd.root.mnt)
  282. path_put(&nd.root);
  283. return ERR_PTR(error);
  284. }
  285. nd.flags &= ~LOOKUP_PARENT;
  286. /*检查最后一段文件或目录名的属性情况*/
  287. if (nd.last_type == LAST_BIND)
  288. goto ok;
  289. error = -EISDIR;
  290. if (nd.last_type != LAST_NORM)
  291. goto exit;
  292. if (nd.last.name[nd.last.len]) {
  293. __putname(nd.last.name);
  294. goto exit;
  295. }
  296. error = -ELOOP;
  297. /*出现回环标志: 循环超过32次*/
  298. if (count++==32) {
  299. __putname(nd.last.name);
  300. goto exit;
  301. }
  302. dir = nd.path.dentry;
  303. mutex_lock(&dir->d_inode->i_mutex);
  304. /*更新路径的挂接点和dentry*/
  305. path.dentry = lookup_hash(&nd);
  306. path.mnt = nd.path.mnt;
  307. __putname(nd.last.name);
  308. goto do_last;
  309. }
  310. 在内核中要打开一个文件,首先应该找到这个文件,而查找文件的过程在vfs里面是由do_path_lookup或者path_lookup_open函数来完成的,关于文件路径查找在前面已经分析过相关的代码了。这两个函数将用户传进来的字符串表示的文件路径转换成一个dentry结构,并建立好相应的inode和file结构,将指向file的描述符返回用户。用户随后通过文件描述符,来访问这些数据结构。

Linux虚拟文件系统之文件打开(sys_open())相关推荐

  1. 网络与IO知识扫盲(一):Linux虚拟文件系统,文件描述符,IO重定向

    系统IO原理 在 Linux 中: VFS(Virtual Filesystem Switch):虚拟文件系统,是一个目录树.树上不同的节点可以映射到物理的文件地址,可以挂载. 相当于一个解耦层,在具 ...

  2. Linux虚拟文件系统、文件描述符、管道

    在Linux中,所有内容都是以文件的形式保存和管理的,即一切皆文件,普通文件是文件,目录(Windows下称为文件夹)是文件,硬件设备(键盘.监视器.硬盘.打印机等)都是文件,就连套接字(socket ...

  3. linux 目录防篡改,一种基于Linux虚拟文件系统的防篡改方法及系统的制作方法

    一种基于Linux虚拟文件系统的防篡改方法及系统的制作方法 [技术领域] [0001]本发明涉及文件防护技术领域,特别涉及一种基于Linux虚拟文件系统的防篡改方法及系统. [背景技术] [0002] ...

  4. Linux虚拟文件系统:数据结构与文件系统注册、文件打开读写

    数据结构 超级块 - super_block 索引节点 - inode 目录项 - dentry 文件结构 - file 虚拟文件系统实现 注册文件系统 - register_filesystem 打 ...

  5. Linux虚拟文件系统

    从文件 I/O 看 Linux 的虚拟文件系统 1 引言 Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux ...

  6. linux虚拟文件系统浅析

    linux虚拟文件系统浅析 虚拟文件系统(VFS) 在我看来, "虚拟"二字主要有两层含义: 1, 在同一个目录结构中, 可以挂载着若干种不同的文件系统. VFS隐藏了它们的实现细 ...

  7. linux 文件系统 vfs,linux虚拟文件系统vfs

    <操作系统>课程设计报告课程设计题目:操作系统课程设计 设计时间:2016/1/10一. 课程设计目的与要求需要完成的内容:(1) 安装虚拟机:Vmware.Vmware palyer ( ...

  8. Linux 虚拟文件系统(一)概述

    Linux 虚拟文件系统(一)概述 tags: Linux源码 Linux 虚拟文件系统一概述 文章梗概 正文 文件系统 虚拟文件系统架构 虚拟文件系统如何知道可用的文件系统有哪些的 不太喜欢的环节 ...

  9. Linux 虚拟文件系统四大对象:超级块、inode、dentry、file之间关系

    一:文件系统 1. 什么是文件系统? 操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统. 通常文件系统是用于存储和组织文件的一种机制,便于对文件进行方便的查找与访问. 文件系统 ...

  10. linux虚拟文件系统-文件的打开

    本质上,一个文件打开的过程就是建立fd,file,dentry,inode,address_space的关联过程.关联过程中关键的一个过程是如何根据路径名寻找到对应的dentry,对于用户程序来说路径 ...

最新文章

  1. python不断刷新网页_python多线程不断刷新网页的代码
  2. ubuntu idea桌面快捷方式无法启动_每个 Ubuntu 用户都应该知道的键盘快捷键
  3. 微软推出 .NET 官方社区论坛
  4. gerrit Cannot Merge
  5. android 内存检测框架,Android项目内存泄漏检测
  6. 输入控件控制输入限制
  7. 吃透这份pdf,拿下阿里、腾讯等大厂心仪offer(软件测试岗)
  8. 如何构造大数据分析平台
  9. 48.怎样消除桌面图标上的小箭头:
  10. JS设计模式 - 工厂模式
  11. mocha java mv_代码覆盖Mocha
  12. 在Ubuntu中获取奥比中光的深度值 Python代码
  13. Unity实用小工具或脚本——可折叠伸缩的多级(至少三级)内容列表(类似于Unity的Hierarchy视图中的折叠效果)
  14. 高新技术企业减税新增政策
  15. EMG电力液压推动器ED80/6
  16. PS学习之合成特效:被风沙侵蚀的动物们
  17. 正版商城 -Microsoft Office 365 家庭版拼团(1用户) 安全可靠,有保障。
  18. Linux基本命令Linux基本命令
  19. HP LaserJet MFP M227-M231 scan use manual
  20. 新疆二级计算机考试地点,2018年3月新疆计算机二级考试准考证打印时间

热门文章

  1. @Autowired与@Resource用法
  2. Linux安装virgo
  3. 迭代器模式(Iterator)和组合模式(Composite)
  4. WebApi的调用-1.前端调用
  5. Java 异常类层次结构
  6. 通过YAJL生成json语句
  7. 跟着辛星认识一下PHP的自己主动载入
  8. 巧用层次坐标解决统计图分类轴与系列取值
  9. 保留源 XAML 中空白的2种方式
  10. hibernate 实现数据库查询