一个更简单的方法上面的例子包含了大段的模板代码。这些代码对于很多应用程序来说都是必须的,但是对于很多其他的,有捷径可走。如果在编译的时候你就知道需要创建哪些文件,并且你没有必要创建子目录,请继续看更简单的方式。
在这部分,我们将讨论lwnfs模块的另一版本,它省掉了约1/3的代码量。它实现了一个不包含子目录,只包含四个计数器的简单序列(array)。再说一遍,如果你有兴趣,就在这里。
上面,我们看到了一个叫做lfs_fill_super()的函数,它负责初始化文件系统的超级块,创建根目录和导出文件。在这个更简单的版本里面,函数整体将变成如下:
static int lfs_fill_super(struct super_block *sb, void *data, int silent) { return simple_fill_super(sb, LFS_MAGIC, OurFiles); } |
simple_fill_super()是libfs中的一个函数,它几乎做了我们需要的所有工作。它的原型是:
int simple_fill_super(struct super_block *sb, int magic, struct tree_descr *files); |
参数super_block结构体可以直接传入,并且magic和我们上面见过的幻数(magic number)相同。参数files描述了我们应该在这个文件系统中创建哪些文件;相关结构定义如下:
struct tree_descr { char *name; struct file_operations *ops; int mode; }; |
到目前为止,那些参数应该相当清楚了;每个结构体给出将要创建的文件的文件名,与这个文件相关的操作函数集,和这个文件的保护位。但是,我们仍有必要看看tree_descr这个结构体是如何创建的:
- 那些被初始化为NULL的入口项(更确切地说是name为NULL)将被简单地忽略掉。不要试图用一个NULL的入口项结束,除非你想解码OOps消息。
- 相反,这个列表(list)是以一个name项为空字符串的入口项结束的。
- 这些入口项直接和最终被赋给目标文件的inode号是一致的。这些信息可以用来在文件的操作代码中获知当前打开的是哪个文件。但是,这也隐含了这个列表的第一项不可用,因为文件系统的根目录将占用0号inode。因此,当你创建tree_descr列表的时候,第一项必须为NULL。
明白了以上知识之后,我们将用下面的方式为那四个“counter”文件创建列表:
static struct tree_descr OurFiles[] = { { NULL, NULL, 0 }, /* Skipped */ { .name = "counter0", /* Inode 1 */ .ops = &lfs_file_ops, .mode = S_IWUSR|S_IRUGO }, { .name = "counter1", /* Inode 2 */ .ops = &lfs_file_ops, .mode = S_IWUSR|S_IRUGO }, { .name = "counter2", /* Inode 3 */ .ops = &lfs_file_ops, .mode = S_IWUSR|S_IRUGO }, { .name = "counter3", /* Inode 4 */ .ops = &lfs_file_ops, .mode = S_IWUSR|S_IRUGO }, { "", NULL, 0 } /* Terminates the list */ }; |
一旦simple_fill_super()返回,大功告成,文件系统也可用了。省下的唯一一个细节也许就是你的open()方法;如果你有多个文件共享同一个file_operations结构体,你将需要指出当前操作的是哪个。这里的关键就在于inode号,也就是i_ino成员。修改过的lfs_open()通过以下方式找到正确的计数器:
static int lfs_open(struct inode *inode, struct file *filp) { if (inode->i_ino > NCOUNTERS) return -ENODEV; /* Should never happen. */ filp->private_data = counters + inode->i_ino - 1; return 0; } |
函数read()和write()用成员private_data_field,因此我们没有必要修改上个版本的实现。
结论这里仅作为libfs示例,实际上libfs的代码被广泛应用于实现驱动特定的虚拟文件系统。如想深入学习,请参考2.5内核源码的如下几个地方:
- drivers/hotplug/pci_hotplug_core.c
- drivers/usb/core/inode.c
- drivers/oprofile/oprofilefs.c
- fs/ramfs/inode.c
- fs/nfsd/nfsctl.c (simple_fill_super() 示例)
...和其他一些地方--grep是你的好朋友!
请记住,2.6的驱动模型代码使得用自己的虚拟文件系统导出信息变得容易;就很多应用而言,它将成为把信息导到用户空间的较好方法。有很多关于驱动模型和sysfs的文章。但是,在只有一个定制的文件系统才能奏效的情况下,libfs使得这项工作变得(相对)简单。
译者注:
以前有人问我哪里能获得有关Linux变化和发展的介绍性文章,我无言以对。现在,总算知道了些有较好技术文章的网站,比如说lwn,linuxjournal和IBM的developer works。
这篇文章最开始写于2003年11月11号(似乎是光棍节),可我现在才注意到,真是感觉有些落伍啊!不敢独享,遂翻译出来与诸君共同把玩!
本想用一年时间熟悉Linux内核的开发,知其然,可随着时间的推进,发现其实知其然和知其所以然是相辅相成的,窃喜!
BTW: 翻译东西还真的很不简单啊,不要笑我哦!
阅读(2292) | 评论(0) | 转发(0) |