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