<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></title> 
<link>http://www.jackxiang.com/index.php</link> 
<description><![CDATA[赢在IT，Playin' with IT,Focus on Killer Application,Marketing Meets Technology.]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></copyright>
<item>
<link>http://www.jackxiang.com/post//</link>
<title><![CDATA[Linux 虚拟文件系统概观 ]]></title> 
<author>jack &lt;xdy108@126.com&gt;</author>
<category><![CDATA[WEB2.0]]></category>
<pubDate>Wed, 13 Apr 2011 16:44:53 +0000</pubDate> 
<guid>http://www.jackxiang.com/post//</guid> 
<description>
<![CDATA[ 
	<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Linux 虚拟文件系统概观<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;作者 Richard Gooch &lt;rgooch在atnf.cs.iro.au&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;翻译 albcamus &lt; albcamus在gmail.com &lt;mailto:albcamus在gmail.com&gt; &gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;最后更新:2005 年 10 月 28 日<br/>&nbsp;&nbsp;&nbsp;&nbsp;版权:1999, Richard Gooch<br/>&nbsp;&nbsp;&nbsp;&nbsp;版权:2005, Pekka Enberg<br/>&nbsp;&nbsp;&nbsp;&nbsp;本文件以 GPLv2 许可发放<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;[翻译说明:Richard Gooch 的这份 Overview of the Linux Virtual File System 位于内核<br/>&nbsp;&nbsp;&nbsp;&nbsp;源代码的 Documentation/filesystems/下, 文件名是 vfs.txt。 该文档在 Understanding<br/>&nbsp;&nbsp;&nbsp;&nbsp;the Linux Kernel 中被推荐, 我是从 2.6.16 官方内核中拷贝出来翻译的。 自己知道水平<br/>&nbsp;&nbsp;&nbsp;&nbsp;差, 理解和表达都不到位, 所以还是建议看原文:( ]<br/><br/><br/>介绍<br/>====<br/><br/>虚拟文件系统(也被称做虚拟文件系统开关)是内核中为应用程序提供接口的一层软件。<br/>同样, 它也提供了一种抽象, 来允许不同的文件系统共存。<br/><br/>VFS 系统调用, 如 open(2),stat(2),read(2),write(2),chmod(2)等, 是在进程上下文被<br/>调用的。 关于文件系统锁的描述在 Documnetation/filesystems/Locking 中。<br/><br/><br/>目录项缓存(dcache)<br/>-----------------<br/><br/>VFS 实现了诸如 open(2),stat(2),chmod(2)等系统调用。 路径名作为参数传递给这些系统<br/>调用, 然后 VFS 使用路径名来在目录项缓存(也叫 dentry cache 或 dcache)中查找相关的<br/>目录项。 这提供了非常迅速的机制来把路径名(文件名)转换成具体的目录项。 目录项只<br/>存在于 RAM 中, 永不会保存到磁盘上:它们的意义只在于性能。<br/><br/>目录项缓存是为了保留一个视域, 一个整个文件空间的缩影。 因为大多数计算机无法同时<br/>把所有的目录项全部保留在 RAM 中, 有些目录项不被缓存。 为了把路径名解析成目录项,<br/>VFS 得一步步解析路径名的同时创建 dentries,最终找到 inode。 这是 inode 查找时做的。<br/><br/><br/>Inode 对象<br/>---------<br/><br/>一个 dentry 通常包含指向 inode 的指针。 Inodes 是诸如常规文件,FIFO 和其他的文件系<br/>统对象。 它们或者存在于磁盘上(块设备上的文件系统), 或者存在于内存中(伪文件系统)。 <br/>当需要时, 在磁盘上的 inode 会被加载到内存中; 对 inode 的改变会写回到磁盘 上。 一个 <br/>inode 可以被多个 dentry 指向(例如硬链接就是这样)。 <br/><br/>为了查找一个 inode, VFS 需要调用它的父目录 inode 的 lookup()方法。 该方法由 inode<br/>所在的文件系统实现来定义。 一旦 VFS 知道了 dentry(因此也就知道了 inode), 我们就<br/>可以执行诸如用 open(2)来打开它、用 stat(2)来查看 inode 内的数据。 Stat(2)操作非常<br/>简单: 一旦 VFS 找到了 dentry, 它就查看相关的 inode 并把部分数据返回给用户空间。<br/><br/><br/>File 对象<br/>--------<br/><br/>打开文件时需要另一个操作: 分配一个 file 结构(就是内核对文件描述符的实现)。 这<br/>个新分配的 file 结构, 会用指向 dentry 的指针和一组文件操作成员函数来初始化──这<br/>是从 inode 取来的。 然后调用文件操作中的 open(2)方法, 这样, 由文件系统自己实现的 <br/>open 就被调用。 File 结构被放置在进程的文件描述符表中。<br/><br/>读、写和关闭文件(以及其他与 VFS 有关的操作)时, 首先使用用户空间的文件描述符找<br/>到相应的 file 结构, 然后执行该结构中定义的相关操作。 只要文件还在打开着, dentry<br/>就处于使用状态, 也就意味着 inode 处在使用状态。<br/><br/><br/>注册和挂载一个文件系统<br/>=====================<br/><br/>注册或注销一个文件系统, 使用如下的 API 函数:<br/><br/>&nbsp;&nbsp; #include &lt;linux/fs.h&gt;<br/><br/>&nbsp;&nbsp; extern int register_filesystem(struct file_system_type *);<br/>&nbsp;&nbsp; extern int unregister_filesystem(struct file_system_type *);<br/><br/>传递进来的 file_system_type 结构描述了一个文件系统。 当有请求到来, 要把某设备挂 <br/>载到文件空间的某个目录上时, VFS 调用相关文件系统(由 file_system_type 结构代表)<br/>的 get_sb()方法。 挂载点的 dentry 将被更新, 以便指向新的文件系统的根 inode。<br/><br/>在/proc/filesystems 文件中, 你可以看到内核中所有已注册的文件系统。<br/><br/><br/>file_system_type 结构<br/>---------------------<br/><br/>该结构描述了文件系统。 例如 2.6.13 内核中的代码, 该结构具有下列成员:<br/><br/>struct file_system_type &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;const char *name;<br/>&nbsp;&nbsp;&nbsp;&nbsp;int fs_flags;<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*get_sb) (struct file_system_type *, int,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char *, void *, struct vfsmount *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*kill_sb) (struct super_block *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;struct module *owner;<br/>&nbsp;&nbsp;&nbsp;&nbsp;struct file_system_type *next;<br/>&nbsp;&nbsp;&nbsp;&nbsp;struct list_head fs_supers;<br/>&#125;;<br/><br/>&nbsp;&nbsp;name: 文件系统的名字, 例如&quot;ext2&quot;、&quot;iso9660&quot;、&quot;msdos&quot;等<br/><br/>&nbsp;&nbsp;fs_flags: 各种标志(亦即: FS_REQUIRES_DEV, FS_NO_DCACHE 等) <br/><br/>&nbsp;&nbsp;get_sb: 每当该类型的文件系统被挂载时, 调用该方法<br/><br/>&nbsp;&nbsp;kill_sb: 每当该类型的文件系统被卸载时, 调用该方法<br/><br/>&nbsp;&nbsp;owner: VFS 内部使用:多数情况下该被赋值为 THIS_MODULE<br/><br/>&nbsp;&nbsp;next: VFS 内部使用:多数情况下该被赋值为 NULL<br/><br/>get_sb()方法有如下参数:<br/><br/>&nbsp;&nbsp;struct super_block sb: superblock 结构。 该结构由 VFS 初始化一部分, 剩下的部分 <br/>&nbsp;&nbsp;&nbsp;&nbsp;由get_sb()方法来初始化。<br/><br/>&nbsp;&nbsp;int flags: 挂载标志<br/><br/>&nbsp;&nbsp;const char *dev_name:要挂载的设备名<br/><br/>&nbsp;&nbsp;void *data: 任意的挂载选项, 通常是 ASCII 字符串的形式<br/><br/>&nbsp;&nbsp;int silent: 出错时是否打印错误信息<br/><br/>get_sb()方法必须探测 superblock 中指定的块设备所包含的文件系统类型, 自己是否支持 <br/>它。 如果成功, 就返回指向 superblock 的指针, 失败则返回 NULL。<br/><br/>get_sb()方法必须填充的 superblock 结构的一些域, 其中 s_op 最值得关注。 它是一个指<br/>向&quot;struct super_operations&quot;的指针, 这个结构描述了文件系统的下层实现。<br/><br/>通常, 一个文件系统会使用通用的 get_sb()实现并自己提供一个 fill_super()方法。 通用 <br/>的 get_sb()实现有:<br/><br/>&nbsp;&nbsp;get_sb_bdev: 挂载一个基于块设备的文件系统<br/><br/>&nbsp;&nbsp;get_sb_nodev: 挂载不存在于磁盘上的文件系统<br/><br/>&nbsp;&nbsp;get_sb_single: 挂载一个与其他挂载共享的文件系统<br/><br/>fill_super()方法具有下列参数:<br/><br/>&nbsp;&nbsp;struct super_block *sb: superblock 结构, fill_super()方法必须初始化它 <br/><br/>&nbsp;&nbsp;void *data: 任意的挂载选项, 通常由 ASCII 字符串组成<br/><br/>&nbsp;&nbsp;int silent: 出错时是否打印错误信息<br/><br/><br/>Superblock 对象<br/>==============<br/><br/>一个 superblock 对象代表一个挂载的文件系统。<br/><br/><br/>super_operations 结构<br/>---------------------<br/><br/>该结构描述了 VFS 如何操作文件系统上的 superblock, 以 2.6.13 为例, 它具有下列成员:<br/><br/>struct super_operations &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;struct inode *(*alloc_inode)(struct super_block *sb);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*destroy_inode)(struct inode *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*read_inode) (struct inode *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*dirty_inode) (struct inode *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*write_inode) (struct inode *, int);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*put_inode) (struct inode *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*drop_inode) (struct inode *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*delete_inode) (struct inode *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*put_super) (struct super_block *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*write_super) (struct super_block *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*sync_fs)(struct super_block *sb, int wait);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*write_super_lockfs) (struct super_block *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*unlockfs) (struct super_block *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*statfs) (struct dentry *, struct kstatfs *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*remount_fs) (struct super_block *, int *, char *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*clear_inode) (struct inode *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*umount_begin) (struct super_block *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*sync_inodes) (struct super_block *sb,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct writeback_control *wbc);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*show_options)(struct seq_file *, struct vfsmount *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); <br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*quota_write)(struct super_block *, int, const char *, size_t,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loff_t);<br/>&#125;;<br/><br/>除非特别说明, 否则所有这些成员都可以在不持有锁的情况下调用, 这意味着这些方法可<br/>能会阻塞, 都必须在进程上下文调用(亦即:不是在中断处理函数或者 boottom half 中)。<br/><br/>&nbsp;&nbsp;alloc_inode: 由 inode_alloc()调用, 来为 inode 结构分配空间并初始化它。 如果不定义该 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;方法，则分配一个简单的inode结构。 而正常情况下，alloc_inode会分配一个更大的<br/>&nbsp;&nbsp;&nbsp;&nbsp;inode结构，它包含那个&quot;简单的inode结构&quot;。<br/><br/>&nbsp;&nbsp;destroy_inode: 由 destroy_inode()调用, 撤消为 inode 分配的资源。 该方法只有在<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alloc_inode被定义的时候才有效，它撤消alloc_inode做的所有事。<br/><br/>&nbsp;&nbsp;read_inode: 从某个挂载的文件系统中读取相关的 inode。 inode 结构的 i_ino 成员由 VFS<br/>&nbsp;&nbsp;&nbsp;&nbsp;初始化, 来指定要读取的 inode; 其他成员由本方法填充。<br/><br/>&nbsp;&nbsp;dirty_inode: 由 VFS 调用, 来把一个 inode 标记为脏。<br/><br/>&nbsp;&nbsp;write_inode: 当 VFS 需要把某个 inode 写入到磁盘上时, 调用本方法。 第二个参数指定<br/>&nbsp;&nbsp;&nbsp;&nbsp;了写操作是否需要同步, 并非所有的文件系统都会检查这个标志。<br/><br/>&nbsp;&nbsp;put_inode: 当 VFS inode 从 inode cache 中移除时调用<br/><br/>&nbsp;&nbsp;drop_inode: 当所有能访问到 inode 的途径都被移除时调用, 调用时必须持有 inode_lock<br/>&nbsp;&nbsp;&nbsp;&nbsp;自旋锁。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;该方法或者为 NULL(正常的 Unix 文件系统语义), 或者为 &quot;generic_delete_inode&quot; <br/>&nbsp;&nbsp;&nbsp;&nbsp;(那些不想 cache inode 的文件系统。 这会导致不管 i_nlink 的值是多少, <br/>&nbsp;&nbsp;&nbsp;&nbsp;delete_inode 总会被调用)<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&quot;generic_delete_inode&quot;的行为, 与 put_inode()中使用&quot;force_delete&quot;是等价的, 但<br/>&nbsp;&nbsp;&nbsp;&nbsp;不象后者那样会引发竞争情形。<br/><br/>&nbsp;&nbsp;delete_inode: 当 VFS 想删除一个 inode 时调用 <br/><br/>&nbsp;&nbsp;put_super: 当 VFS 想释放 superblock(亦即卸载)时调用。 应持有 superblock 自旋锁。<br/><br/>&nbsp;&nbsp;wirte_super: 当 VFS 需要写入到磁盘时调用, 该方法是可选的。<br/><br/>&nbsp;&nbsp;sysc_fs: 当 VFS 正在把所有与 superblock 有关的脏数据写入到磁盘上时调用。 第二个参<br/>&nbsp;&nbsp;&nbsp;&nbsp;数指示了该方法是否需要一直等待写操作的完成。 可选。<br/><br/>&nbsp;&nbsp;write_super_lockfs: 当 VFS 正锁定一个文件系统, 强制它进入一致状态时, 调用该方<br/>&nbsp;&nbsp;&nbsp;&nbsp;法。 该方法目前用于逻辑卷管理(Logical Volume Manager, LVM)<br/><br/>&nbsp;&nbsp;unlockfs: 当 VFS 解锁一个文件系统, 并标记它为可写的, 此时调用本方法。<br/><br/>&nbsp;&nbsp;statfs: 当 VFS 想获得文件系统的一些统计数据时调用。 调用时需要持有内核锁(翻译疑 <br/>&nbsp;&nbsp;&nbsp;&nbsp;问:看 2.6.16 的 vfs_statfs 函数调用 sb-&gt;s_op-&gt;statfs 时并没有持有锁, 不知道<br/>&nbsp;&nbsp;&nbsp;&nbsp;作者指的是哪把锁?)<br/><br/>&nbsp;&nbsp;remount_fs: 当文件系统被 remount 时调用, 调用需持有内核锁<br/><br/>&nbsp;&nbsp;clear_inode: 当 VFS 清除 inode 时调用。 可选。<br/><br/>&nbsp;&nbsp;umount_begin: 当 VFS 卸载一个文件系统时调用 <br/><br/>&nbsp;&nbsp;sysc_inodes: 当 VFS 正把与 superblock 相关的脏数据写到磁盘上时调用<br/><br/>&nbsp;&nbsp;show_options: VFS 需要在/proc/&lt;pid&gt;/mounts 显示挂载选项时调用<br/><br/>&nbsp;&nbsp;quota_read: VFS 想读取文件系统的磁盘配额文件时调用<br/><br/>&nbsp;&nbsp;quota_write: VFS 想写入文件系统的磁盘配额文件时调用<br/><br/>read_inode()方法负责填充&quot;i_ip&quot;域, 它是一个指向&quot;struct inode_operations&quot;的指针, 该 <br/>结构描述了那些操作于每个 inode 的方法。<br/><br/><br/>Inode 对象<br/>==========<br/><br/>一个 inode 对象代表了文件系统内的一个对象。<br/><br/>inode_operations 结构<br/>---------------------<br/><br/>描述了 VFS 如何操作你的文件系统中的一个 inode。 例如在 2.6.13 内核中, 有如下的成员:<br/><br/>struct inode_operations &#123; <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*create) (struct inode *,struct dentry *,int, struct nameidata *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;struct dentry * (*lookup) (struct inode *,struct dentry *, struct<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nameidata *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*link) (struct dentry *,struct inode *,struct dentry *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*unlink) (struct inode *,struct dentry *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*symlink) (struct inode *,struct dentry *,const char *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*mkdir) (struct inode *,struct dentry *,int);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*rmdir) (struct inode *,struct dentry *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*mknod) (struct inode *,struct dentry *,int,dev_t);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*rename) (struct inode *, struct dentry *,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct inode *, struct dentry *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*readlink) (struct dentry *, char __user *,int); <br/>&nbsp;&nbsp;&nbsp;&nbsp;void * (*follow_link) (struct dentry *, struct nameidata *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*put_link) (struct dentry *, struct nameidata *, void *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*truncate) (struct inode *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*permission) (struct inode *, int, struct nameidata *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*setattr) (struct dentry *, struct iattr *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);<br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); <br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*listxattr) (struct dentry *, char *, size_t);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*removexattr) (struct dentry *, const char *);<br/>&#125;;<br/><br/>正如 super_operations 的方法一样, inode_operations 的成员也可以在无锁情形下调用──<br/>除非有特别说明。<br/><br/>&nbsp;&nbsp;create: 由 open(2)和 creat(2)系统调用来调用。 只有你想在文件系统中支持正规文件时, <br/>&nbsp;&nbsp;&nbsp;&nbsp;才需要提供该方法的实现。 得到的 dentry 不跟 inode 相关连(亦即:是一个负的<br/>&nbsp;&nbsp;&nbsp;&nbsp;dentry)。 一般需要使用 dentry 和新建的 inode 来调用 d_instantiate()函数。<br/><br/>&nbsp;&nbsp;lookup: VFS 需要在父目录中寻找一个 inode 时调用。 要查找的文件名在 dentry 中。 该方<br/>&nbsp;&nbsp;&nbsp;&nbsp;法必须调用 d_add()来把找到的 inode 关联到到 dentry, 该 inode 的&quot;i_count&quot;域随之增 <br/>&nbsp;&nbsp;&nbsp;&nbsp;加。 如果该名字的 inode 未找到, 则 dentry 与 NULL 关联(亦即:是一个负的 dentry)。<br/>&nbsp;&nbsp;&nbsp;&nbsp;本方法只有在真正遇到不可恢复的错误时才返回错误, 否则的话, 那些会创建 inode 的系<br/>&nbsp;&nbsp;&nbsp;&nbsp;统调用, 如 create(2)、mknod(2)、mkdir(2)等将无法工作。 如果你想重载 dentry 的方法,<br/>&nbsp;&nbsp;&nbsp;&nbsp;那么就初始化 dentry 的&quot;d_dop&quot;域, 它是一个指向&quot;struct dentry_operations&quot;的指针。 <br/><br/>&nbsp;&nbsp;link: 由系统调用 link(2)来调用。 只有你想在自己的文件系统内支持硬链接时, 才需要<br/>&nbsp;&nbsp;&nbsp;&nbsp;提供该方法的实现。 多数情形下, 应该象我们刚刚在 create 方法中描述的那样调用<br/>&nbsp;&nbsp;&nbsp;&nbsp;d_instantiate()函数。<br/><br/>&nbsp;&nbsp;unlink: 由 unlink(2)系统调用来调用。 只有你想支持删除 inode 时才提供。<br/><br/>&nbsp;&nbsp;symlink: 由 symlink(2)系统调用来调用。 只有你想在自己的文件系统里支持符号链接时, <br/>&nbsp;&nbsp;&nbsp;&nbsp;才需要提供该方法的实现。 多数情形下, 需要象 create 方法那样调用 d_instantiate()<br/>&nbsp;&nbsp;&nbsp;&nbsp;函数。<br/><br/>&nbsp;&nbsp;mkdir: 由系统调用 mkdir(2)来调用。 只有你想在文件系统中支持创建子目录时, 才需要提供其实<br/>&nbsp;&nbsp;&nbsp;&nbsp;现。 需要象 create 方法那样调用 d_instantiate()函数<br/><br/>&nbsp;&nbsp;rmdir: 由系统调用 rmdir(2)来调用。 只有想支持删除子目录时, 才需要。 <br/><br/>&nbsp;&nbsp;mknod: 由系统调用 mknod(2)来调用, 以创建设备 inode 或者命名管道(FIFO)或者<br/>&nbsp;&nbsp;&nbsp;&nbsp;socket。 只有你想支持创建这些类型的 inode 时, 你的文件系统才需要提供该方法的实现。<br/>&nbsp;&nbsp;&nbsp;&nbsp;需要象 create 方法那样调用 d_instantiate()方法。<br/><br/>&nbsp;&nbsp;rename: 由系统调用 rename(2)来调用, 以重命名一个对象, 使之具有由第二个 inode 和 <br/>&nbsp;&nbsp;&nbsp;&nbsp;denrty 给定的父目录和名字。<br/><br/>&nbsp;&nbsp;follow_link: 由 VFS 调用, 以跟踪符号链接到它所指向的 inode。 只有你想支持符号链接<br/>&nbsp;&nbsp;&nbsp;&nbsp;时才需要提供。 该方法返回了 void 型指针 cookie 传递给 put_link(), 参考下面的 put_link()<br/>&nbsp;&nbsp;&nbsp;&nbsp;方法。<br/><br/>&nbsp;&nbsp;put_link: 由 VFS 调用, 来释放由 follow_link 方法申请的临时性资源。 由follow_link()返回的 <br/>&nbsp;&nbsp;&nbsp;&nbsp;cookie 作为最后一个参数传递给本方法。 由一些诸如 NFS 这样的文件系统使用, 因为在这样的文<br/>&nbsp;&nbsp;&nbsp;&nbsp;件系统中, page cache 是不稳定的(亦即, 随着跟踪符号链接的过程中建立起的 page cache 可能在<br/>&nbsp;&nbsp;&nbsp;&nbsp;跟踪的最后已经不存在了)。<br/><br/>&nbsp;&nbsp;truncate: 由 VFS 调用以改变文件的大小。 在调用本方法之前, VFS 先把 inode 的 i_size<br/>&nbsp;&nbsp;&nbsp;&nbsp;域设为期望的值。 本方法主要由 truncate(2)系统调用等使用。 <br/><br/>&nbsp;&nbsp;permission: 由 VFS 调用, 来检查 POSIX 类文件系统的访问权限。<br/><br/>&nbsp;&nbsp;setattr: 由 VFS 调用, 来设定文件的属性, 该方法主要由 chmod(2)等使用<br/><br/>&nbsp;&nbsp;getattr: 由 VFS 调用, 来设定文件的属性, 主要由 stat(2)等系统调用使用<br/><br/>&nbsp;&nbsp;setxattr: 由 VFS 调用, 来设置文件的扩展属性。 所谓扩展属性,就是和 inode 相关的一 <br/>&nbsp;&nbsp;&nbsp;&nbsp;对「名称-值」, 它是在 inode 分配的时候与之关联的。 由 setxattr(2)系统调用使用。<br/><br/>&nbsp;&nbsp;getxattr: 由 VFS 调用, 获取根据扩展属性的名称, 获取其值。 由 getxattr(2)系统调用<br/>&nbsp;&nbsp;&nbsp;&nbsp;使用。<br/><br/>&nbsp;&nbsp;listxattr: 由 VFS 调用, 来列出给定文件的所有扩展属性。 给 listxattr(2)系统调用使用。<br/><br/>&nbsp;&nbsp;removexattr: 由 VFS 调用, 移除给定文件的扩展属性。 给 removexattr(2)系统调用使用。 <br/><br/><br/><br/>地址空间对象(The Address Space Object)<br/>======================================<br/><br/>地址空间对象用来对Page Cache中的页进行分组、管理。 它可以用来跟踪文件中的(或其他地方的)<br/>页面，也可以用来跟踪文件映射到进程地址空间的映射区。<br/><br/>地址空间对象有多种用处，其关系有时并不紧密。 这些用处包括：计算内存的资源紧张程度， <br/>根据地址来查找页面，跟踪那些标记为Dirty和Writeback的页面。<br/><br/>在这用处中，第一项可以独立于其他项单独使用。VM可以把脏页写入磁盘，从而使得它变为clean；<br/>也可以释放clean页，以便重新使用它。 对脏页调用-&gt;writepage方法，和对clean页设置PagePrivate<br/>并调用-&gt;releasepage方法，就可以达到这个目的。 没有被设置PagePrivate的Clean页面，如果又 <br/>没有外部的引用，可以直接释放，而不用通知它所属的地址空间对象。<br/><br/>为了做到这点，应该把页面放入一个LRU链表，每当页面被使用，就调用lru_cache_add和<br/>mark_page_active。<br/><br/>正常情况下页面要位于基树(radix tree)中，由page结构的-&gt;index成员来索引。该基树为每个<br/>页面维护了PG_Dirty和PG_Writeback的信息，这样，设置了这些标志的页面可以通过基树很快 <br/>找到。<br/><br/>Dirty标签主要是由mpage_writeages──默认的-&gt;writepages方法──使用的，用来寻找那么需要调用<br/>-&gt;writepages方法的页面。 如果未使用mpage_writepages函数(亦即，地址空间提供了自己定义的<br/>-&gt;writepages方法)，PAGECACHE_TAG_DIRTY标签就基本没用了。 write_inode_now和sync_inode函 <br/>数倒是会使用它(通过__sync_single_inode)，来检查-&gt;writepages方法是否成功把地址空间对象里<br/>的所有脏页都写出去了。<br/><br/>Writeback标签由 filemap*_wait* 和 sync_page* 等函数使用(通过wait_on_page_writeback_range)，<br/>以等待写回操作完成。 在等待的时候，会针对每个需要写回的页面调用-&gt;sync_page方法(如果定义了)。 <br/><br/>地址空间对象的操作可能会给页面附加一些信息，使用&#039;struct page&#039;的&#039;private&#039;。 如果附加了这样<br/>的信息，那么就应该设置页面的PG_Private标志，这样，各个VM子系统就会在相应的场合调用地址空<br/>间对象的处理函数来处理这些附加信息。<br/><br/>地址空间对象是联系应用程序和磁盘存储的媒介。 数据是以页为单位，从存储读入地址空间的；而提供<br/>给应用程序时，或者是拷贝该页，或者是对该页进行内存映射。 数据是由应用程序写入到地址空间的， <br/>然后写回到磁盘存储--通常也是以页为单位，但实际上地址空间对写操作的大小有控制权。<br/><br/>读的过程很简单，只需要&#039;readpage&#039;方法；相对来说，写的过程要复杂的多，使用prepare_write/<br/>commit_write或set_page_dirty来把数据写入到地址空间中，再使用writepage,sync_page和writepages<br/>来把数据从地址空间中写入到磁盘存储上。<br/><br/>从地址空间中添加和删除页面，都必须得持有inode的i_mutex互斥锁。<br/><br/>当有数据写入页面，就应该页面的PG_Dirty标志。 该标志一直保持着，直到writepage请求把该页写回存<br/>储--这回清除PG_Dirty标志，而设置PG_Writeback标志。只要PG_Dirty标志清除了，就可以往该页写数据，<br/>在合适的时机PG_Writeback标志也会被清除。<br/><br/>Writeback使用了一个writeback_control结构。 <br/><br/><br/><br/>address_space_operations结构<br/>----------------------------<br/><br/>该结构描述了 VFS 如何把文件映射到 page cache 中。 例如在 2.6.16 内核中, 它有以下成<br/>员:<br/><br/>struct address_space_operations &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*writepage)(struct page *page, struct writeback_control *wbc); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*readpage)(struct file *, struct page *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*sync_page)(struct page *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*writepages)(struct address_space *, struct writeback_control *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*set_page_dirty)(struct page *page); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*readpages)(struct file *filp, struct address_space *mapping,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct list_head *pages, unsigned nr_pages);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*commit_write)(struct file *, struct page *, unsigned, unsigned); <br/>&nbsp;&nbsp;&nbsp;&nbsp;sector_t (*bmap)(struct address_space *, sector_t);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*invalidatepage) (struct page *, unsigned long);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*releasepage) (struct page *, int);<br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loff_t offset, unsigned long nr_segs);<br/>&nbsp;&nbsp;&nbsp;&nbsp;struct page* (*get_xip_page)(struct address_space *, sector_t,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int);<br/>&nbsp;&nbsp;&nbsp;&nbsp;/* migrate the contents of a page to the specified target */<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*migratepage) (struct page *, struct page *); <br/>&#125;;<br/><br/>&nbsp;&nbsp;writepage: 由 VM 用来把脏页写到磁盘上。 这可能是为了数据的完整性(亦即，&#039;sync&#039;)，<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;或者是为了释放内存(flush)。二者的区别见wbc-&gt;sync_mode。<br/>&nbsp;&nbsp;&nbsp;&nbsp;writepage方法调用前应保证PG_Dirty标志已清除，PageLocaked已设置。该方法应<br/>&nbsp;&nbsp;&nbsp;&nbsp;调用writeout函数，并在写操作完成时设置PG_Writeback标志，并保证页面未被锁。 <br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;如果wbc-&gt;syn_mode的值为WB_SYNC_NONE， -&gt;writepage方法就不必费力的完成工作，<br/>&nbsp;&nbsp;&nbsp;&nbsp;它可以选择容易写的其他页面(例如，由于内部依赖的问题)。 如果它选择不调用<br/>&nbsp;&nbsp;&nbsp;&nbsp;writeout函数，则应当返回AOP_WRITEPAGE_ACTIVE，以便VM不再对该页调用<br/>&nbsp;&nbsp;&nbsp;&nbsp;-&gt;writepage方法了。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;更多细节可以参考&quot;Locking&quot;文件。 <br/><br/>&nbsp;&nbsp;readpage: 由 VM 用来从磁盘上读取页面。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当readpage方法应该在已经对页面加锁的情形下调用，而且函数完成时要把页面设置为<br/>&nbsp;&nbsp;&nbsp;&nbsp;未锁状态，并标记为最新。 如果由于某种原因，-&gt;readpage方法需要解锁该页，它倒<br/>&nbsp;&nbsp;&nbsp;&nbsp;也可以这么做，然后返回AOP_TRUNCATED_PAGE。 这种情况下，该页将被重定位、重新<br/>&nbsp;&nbsp;&nbsp;&nbsp;加锁，都成功了再重新调用-&gt;readpage方法。 <br/><br/>&nbsp;&nbsp;sync_page: VM 调用它来通知磁盘, 执行所有与某一页有关的正等待的 I/O 操作。 同一<br/>&nbsp;&nbsp;&nbsp;&nbsp;address_space 中的其他页的 I/O 也可能被执行。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;该方法是可选的，只有在等待writeback完成时才会调用，而且只针对那些设置了<br/>&nbsp;&nbsp;&nbsp;&nbsp;PG_Writeback的页调用。<br/><br/>&nbsp;&nbsp;writepages;&nbsp;&nbsp;VM 调用, 把跟该 address_space 有关的页写到磁盘。 如果wbc-&gt;sync_mode <br/>&nbsp;&nbsp;&nbsp;&nbsp;为WBC_SYNC_ALL，那么writeback_control将一系列的页面指定为需要写出去。如果是<br/>&nbsp;&nbsp;&nbsp;&nbsp;WBC_SYNC_NONE，就要给一个nr_to_write值，有这么多的页面需要写出去。<br/>&nbsp;&nbsp;&nbsp;&nbsp;如果未提供-&gt;writepages方法，那么就使用mpage_writepages函数。它会选择地址空间<br/>&nbsp;&nbsp;&nbsp;&nbsp;对象中那些标记为脏的页面，并传给-&gt;write_page方法。<br/><br/>&nbsp;&nbsp;set_page_dirty: VM调用它把某页标记为脏。<br/>&nbsp;&nbsp;&nbsp;&nbsp;如果一个地址空间对象给页面附加了数据，且当页面变脏时那些数据需要更新，那么就需要<br/>&nbsp;&nbsp;&nbsp;&nbsp;本方法了。 例如，当一个内存映射的页被修改了，本方法就被调用。<br/>&nbsp;&nbsp;&nbsp;&nbsp;如果提供了本方法，它需要设置页的PageDirty标志，也设置基树的PAGECACHE_TAG_DIRTY<br/>&nbsp;&nbsp;&nbsp;&nbsp;标签。<br/><br/>&nbsp;&nbsp;readpages: VM 调用, 从磁盘上读取跟该 address_space 有关的页。 <br/>&nbsp;&nbsp;&nbsp;&nbsp;基本可以说本方法就是readpage方法的向量化版本(a vector version of readpage)。<br/>&nbsp;&nbsp;&nbsp;&nbsp;readpage方法只读一个页面，而本方法读多个页面。<br/>&nbsp;&nbsp;&nbsp;&nbsp;readpages方法只用与预读(read-ahead)，所以其错误可以被忽略，不管什么地方出错<br/>&nbsp;&nbsp;&nbsp;&nbsp;了，只管放弃，没问题的。<br/><br/>&nbsp;&nbsp;prepare_write:&nbsp;&nbsp;在通用写操作路径中, VM 调用它来设置跟页有关的写请求。 <br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/><br/>&nbsp;&nbsp;commit_write: 在通用写操作路径中, VM 调用它来把页写入到磁盘上。<br/><br/>&nbsp;&nbsp;bmap: VFS 调用它, 把对象内的逻辑偏移映射到物理块号上。 传统的 FIBMAP ioctl 系统调<br/>&nbsp;&nbsp;&nbsp;&nbsp;用使用该函数, 其他场合不赞成使用该函数。<br/><br/>&nbsp;&nbsp;invalidatepage: Vm 调用它,断开某页与其 address_space 的映射关系。<br/><br/>&nbsp;&nbsp;releasepage: VFS 调用它, 释放页中特定于文件系统的元数据。 <br/><br/>&nbsp;&nbsp;direct_IO: VM 为直接 I/O 的读/写操作调用它<br/><br/>&nbsp;&nbsp;get_xip_page: VM 调用它, 把块号转换成页。 在相关的文件系统卸载之前, 该页保持有<br/>&nbsp;&nbsp;&nbsp;&nbsp;效。 那些想实现「适当执行」(execute-in-place,XIP)的文件系统需要提供该方法的实现。<br/>&nbsp;&nbsp;&nbsp;&nbsp;在 fs/ext2/xip.c 文件中可以找到例子。<br/><br/><br/>文件对象(The File Object) <br/>=========================<br/><br/>一个文件对象, 代表了进程的一个打开文件。<br/><br/>file_operations 结构<br/>--------------------<br/><br/>该结构描述了 VFS 如何操作一个打开的文件。 例如在内核 2.6.13 中, 它有如下成员:<br/><br/>struct file_operations &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;loff_t (*llseek) (struct file *, loff_t, int); <br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);<br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loff_t);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*readdir) (struct file *, void *, filldir_t);<br/>&nbsp;&nbsp;&nbsp;&nbsp;unsigned int (*poll) (struct file *, struct poll_table_struct *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;long);<br/>&nbsp;&nbsp;&nbsp;&nbsp;long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);<br/>&nbsp;&nbsp;&nbsp;&nbsp;long (*compat_ioctl) (struct file *, unsigned int, unsigned long); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*mmap) (struct file *, struct vm_area_struct *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*open) (struct inode *, struct file *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*flush) (struct file *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*release) (struct inode *, struct file *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*fsync) (struct file *, struct dentry *, int datasync); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*aio_fsync) (struct kiocb *, int datasync);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*fasync) (int, struct file *, int);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*lock) (struct file *, int, struct file_lock *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loff_t *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loff_t *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*); <br/>&nbsp;&nbsp;&nbsp;&nbsp;ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int);<br/>&nbsp;&nbsp;&nbsp;&nbsp;unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;long, unsigned long, unsigned long); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*check_flags)(int);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*dir_notify)(struct file *filp, unsigned long arg);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*flock) (struct file *, int, struct file_lock *);<br/>&#125;;<br/><br/>除非特别说明, 否则这些方法都可以在不加锁的情况下调用。<br/><br/>&nbsp;&nbsp;llseek: VFS 想移动文件的读写位置指针时调用 <br/><br/>&nbsp;&nbsp;read: 由 read(2)及其它相关系统调用调用<br/><br/>&nbsp;&nbsp;aio_read: 由 io_submit(2)及其他异步 I/O 操作调用<br/><br/>&nbsp;&nbsp;write: 由 write(2)及相关系统调用调用<br/><br/>&nbsp;&nbsp;aio_write: 由 io_submit(2)及其他异步 I/O 操作调用<br/><br/>&nbsp;&nbsp;readdir: VFS 想读取目录内容时调用<br/><br/>&nbsp;&nbsp;poll: VFS 调用。 调用的时机为: 当进程想检查某一文件上是否出现特定特征, 并且 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(可选地)阻塞, 直到所等待特征出现。 给 select(2)和 poll(2)系统调用使用。<br/><br/>&nbsp;&nbsp;ioctl: 由 ioctl(2)调用<br/><br/>&nbsp;&nbsp;unlocked_ioctl: 由 ioctl(2)调用。 那些并不获取 BKL(译注:Big Kernel Lock, 大内核<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;锁,一种同一时刻只允许一个 CPU 在内核态、允许递归获取的锁,详见 lib/kernel_lock.c <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;代码注释)<br/><br/>&nbsp;&nbsp;compat_ioctl: 由 ioctl(2)调用。 调用时机为: 在 64 位内核上执行 32 位的 ioctl 系统调用。<br/><br/>&nbsp;&nbsp;mmap: 由 mmap(2)调用<br/><br/>&nbsp;&nbsp;open: 当 VFS 想打开一个 inode 时调用。 VFS 打开文件时, 先创建一个新的 struct file, 然后<br/>&nbsp;&nbsp;&nbsp;&nbsp;调用该 file 结构的 open 方法。 嗯, 你可能会想:open 方法为什么不放在 <br/>&nbsp;&nbsp;&nbsp;&nbsp;struct inode_operations 里呢? 可能这种想法也有道理, 但我觉得象内核这样设计, 可以简<br/>&nbsp;&nbsp;&nbsp;&nbsp;化文件系统的实现。 并且, 该 open()方法适合初始化 file 结构的&quot;private_data&quot;成员──如<br/>&nbsp;&nbsp;&nbsp;&nbsp;果你想让该成员指向某个设备的数据结构。<br/><br/>&nbsp;&nbsp;flush: 由 close(2)调用, 来冲刷文件。<br/><br/>&nbsp;&nbsp;release: 当最后一个对 file 结构的指向也被关闭时调用 <br/><br/>&nbsp;&nbsp;fsync: 由 fsync(2)调用<br/><br/>&nbsp;&nbsp;fasync: 由 fcntl(2)调用, 前提是该 file 的异步(非阻塞)模式已被激活<br/><br/>&nbsp;&nbsp;lock: 由带 F_GETLK,F_SETLK 和 F_SETLKW 命令的 fcntl(2)调用<br/><br/>&nbsp;&nbsp;readv: 由 readv(2)调用<br/><br/>&nbsp;&nbsp;writev: 由 writev(2)调用<br/><br/>&nbsp;&nbsp;sendfile: 由 sendfile(2)调用 <br/><br/>&nbsp;&nbsp;get_unmapped_aera: 由 mmap(2)调用<br/><br/>&nbsp;&nbsp;check_flags: 由带 F_SETFL 命令的 fcntl(2)调用<br/><br/>&nbsp;&nbsp;dir_notify: 由带 F_NOTIFY 命令的 fcntl(2)调用<br/><br/>&nbsp;&nbsp;flock: 由 flock(2)调用<br/><br/>&nbsp;&nbsp;注意, 文件操作的这些方法, 是由其 inode 所在的分区的文件系统来实现的。 当打开一<br/>个设备文件(字符设备或块设备特殊文件)时, 多数文件系统会调用 VFS 的一些例程来定<br/>位该设备所属的驱动程序信息。 这些例程将用设备驱动程序中实现的的 file operations<br/>替换文件系统中实现的的那个, 并继续调用新的 open 方法, 这是「为什么打开文件系统<br/>中的设备文件,会最终导致调用设备驱动中的 open()方法」的原因。<br/><br/><br/>目录项 Cache(Directory Entry Cache, dcache) <br/>===========================================<br/><br/><br/>dentry_operations 结构<br/>----------------------<br/><br/>该结构描述了一个文件系统如何重载标准的 dentry 操作集。 Dentry 和 dcache 是 VFS 和具<br/>体文件系统实现的概念, 设备驱动程序就和他们不搭边了。 这些方法可以被置为 NULL,<br/>因为它们是可选的, 如果你不实现, VFS 就使用默认的。 例如 2.6.13 内核中, 该结构有<br/>如下成员:<br/><br/>struct dentry_operations &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*d_revalidate)(struct dentry *, struct nameidata *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*d_hash) (struct dentry *, struct qstr *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*d_compare) (struct dentry *, struct qstr *, struct qstr *); <br/>&nbsp;&nbsp;&nbsp;&nbsp;int (*d_delete)(struct dentry *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*d_release)(struct dentry *);<br/>&nbsp;&nbsp;&nbsp;&nbsp;void (*d_iput)(struct dentry *, struct inode *);<br/>&#125;;<br/><br/>&nbsp;&nbsp;d_revalidate: 当 VFS 想重新使一个 dentry 有效时调用, 这一般发生在某次查找中在dcache <br/>&nbsp;&nbsp;&nbsp;&nbsp;中找到了 dentry。 多数文件系统会把这个方法置为 NULL, 因为它们留在 dache 中的 <br/>&nbsp;&nbsp;&nbsp;&nbsp;dentry 还是有效的。<br/><br/>&nbsp;&nbsp;d_hash: 当 VFS 把一个 dentry 加入到哈希表中时调用<br/><br/>&nbsp;&nbsp;d_compare: 比较两个 dentry 时调用<br/><br/>&nbsp;&nbsp;d_delete: 当 dentry 的最后一个引用被删除时调用。 这意味着没有人在使用这个 dentry<br/>&nbsp;&nbsp;&nbsp;&nbsp;了, 但它依然是有效的, 并且在 dcache 中。<br/>&nbsp;&nbsp;<br/>&nbsp;&nbsp;d_release: 当 dentry 真正被销毁时调用。 <br/><br/>&nbsp;&nbsp;d_input: 当一个 denrty 失去了它所属的 inode 时(正好在 dentry 被销毁之前)调用。 <br/>&nbsp;&nbsp;&nbsp;&nbsp;如果这个方法置为 NULL, VFS 就会调用 iput(); 如果你自己定义了该方法, 必须在自己<br/>&nbsp;&nbsp;&nbsp;&nbsp;的实现中调用 iput()。<br/><br/>每个 dentry 含有一个指向父 dentry 的指针, 还有一个所有子 dentries 的哈希链表。 基<br/>本上, 子 dentries 就象目录中的文件一样。 <br/><br/><br/><br/>Dcache API<br/>----------<br/><br/>内核中定义了许多函数, 供文件系统来操作 dentries:<br/><br/>&nbsp;&nbsp;dget: 打开一个已存在的 dentry 的句柄(在这里,只是增加引用计数而已)<br/><br/>&nbsp;&nbsp;dput: 关闭 dentry 的一个句柄(减少引用计数)。 如果引用计数减到了 0, 就调用<br/><br/>&nbsp;&nbsp;d_delete 方法, 把该 dentry 置入「未使用」队列。 「把 dentry 置入未使用队列」意味着, <br/>&nbsp;&nbsp;&nbsp;&nbsp;如果内存不够用了, 将遍历「未使用队列」并调用 deallocates 方法来销毁 dentries,<br/>&nbsp;&nbsp;&nbsp;&nbsp;以腾出内存。 如果 dentry 已经是「unhashed」(译注:指不在父 dentry 的 hash 链中)且<br/>&nbsp;&nbsp;&nbsp;&nbsp;引用计数为 0, 这时候调用 d_delete 方法然后销毁它。<br/><br/>&nbsp;&nbsp;d_drop: 该方法把一个 dentry 从它的父 dentry 的 hash 链中脱链。 如果它的引用计数变为 <br/>&nbsp;&nbsp;&nbsp;&nbsp;0, 随后的调用 dput()将销毁该 dentry。<br/><br/>&nbsp;&nbsp;d_delete: 删除一个 dentry。 如果该 dentry 没有其他的引用了, 则变为「负的 dentry」<br/>&nbsp;&nbsp;&nbsp;&nbsp;并调用 d_iput()方法; 如果还有其他引用, 就不走这些而调用 d_drop()。<br/><br/>&nbsp;&nbsp;d_add: 把一个 dentry 放入它的父 dentry 的哈希链表, 并调用 d_instantiate()。 <br/><br/>&nbsp;&nbsp;d_instantiate: 把一个 dentry 链入 inode 的「别名哈希链表」并更新 d_inode 域。 inode<br/>&nbsp;&nbsp;&nbsp;&nbsp;结构的i_count 域应该被设置/增加。 如果 dentry 不和任何 inode 关联, 则它就是一个<br/>&nbsp;&nbsp;&nbsp;&nbsp;「负的 dentry」。 该函数一般在为负的 dentry 新创建一个 inode 时调用。<br/><br/>&nbsp;&nbsp;d_lookup: 给出父 dentry 和名字等信息, 在 dcache 哈希表中查找一个 dentry。 如果找到, <br/>&nbsp;&nbsp;&nbsp;&nbsp;增加其引用计数并返回其地址。 调用者在使用完毕时, 必须调用 d_put()方法来释放<br/>&nbsp;&nbsp;&nbsp;&nbsp;dentry。<br/><br/>关于访问 dentry 时加锁的更多信息, 请参考文档 Documentation/filesystems/dentry-locking.txt。<br/><br/><br/><br/>资源列表<br/>========<br/><br/>(注意并非所有资源都适合最新版本的内核)<br/><br/>Creating Linux virtual filesystems. 2002 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;http://lwn.net/Articles/13325/&gt;<br/><br/>The Linux Virtual File-system Layer by Neil Brown. 1999<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt; http://www.cse.unsw.edu.au/~neilb/oss/linux-commentary/vfs.html &lt;http://www.cse.unsw.edu.au/~neilb/oss/linux-commentary/vfs.html&gt; &gt;<br/><br/>A tour of the Linux VFS by Michael K. Johnson. 1996<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;http://www.tldp.org/LDP/khg/HyperNews/get/fs/vfstour.html &lt;http://www.tldp.org/LDP/khg/HyperNews/get/fs/vfstour.html&gt; &gt;<br/><br/>A small trail through the Linux kernel by Andries Brouwer. 2001<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;http://www.win.tue.nl/~aeb/linux/vfs/trail.html&gt;
]]>
</description>
</item><item>
<link>http://www.jackxiang.com/post//#blogcomment</link>
<title><![CDATA[[评论] Linux 虚拟文件系统概观 ]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.jackxiang.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>