Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2033929
  • 博文数量: 369
  • 博客积分: 10093
  • 博客等级: 上将
  • 技术积分: 4271
  • 用 户 组: 普通用户
  • 注册时间: 2005-03-21 00:59
文章分类

全部博文(369)

文章存档

2013年(1)

2011年(2)

2010年(10)

2009年(16)

2008年(33)

2007年(146)

2006年(160)

2005年(1)

分类: LINUX

2006-10-31 21:13:55

一个更简单的方法

上面的例子包含了大段的模板代码。这些代码对于很多应用程序来说都是必须的,但是对于很多其他的,有捷径可走。如果在编译的时候你就知道需要创建哪些文件,并且你没有必要创建子目录,请继续看更简单的方式。

在这部分,我们将讨论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: 翻译东西还真的很不简单啊,不要笑我哦!
阅读(2262) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~