免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1218 | 回复: 0
打印 上一主题 下一主题

The seq_file interface [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-01-04 19:06 |只看该作者 |倒序浏览

4.3.1.4 The seq_file interface
4.3.1.4 seq_file接口
   
    As we noted above, the implementation of large files under /proc is
a little awkward. Over time, /proc methods have become notorious for
buggy implementations when the amount of output grows large. As a way
of cleaning up the /proc code and making life easier for kernel
programmers, the seq_file interface was added. This interface provides
a simple set of functions for the implementation of large kernel
virtual files.
        我们前面说过,大型/proc文件的创建工作有些笨拙。久而久之,/proc方法已渐渐地暴露出在处理大型文件方面的弊端。为了能够取代/proc并简化操作,内核开发者们添加了seq_file接口。这个接口提供了一个简单的函数集专门用来实现大型的内核虚拟文件。
   
    The seq_file interface assumes that you are creating a virtual file
that steps through a sequence of items that must be returned to user
space. To use seq_file, you must create a simple "iterator" object that
can establish a position within the sequence, step forward, and output
one item in the sequence. It may sound complicated, but, in fact, the
process is quite simple. We'll step through the creation of a /proc
file in the scull driver to show how it is done.
        seq_file接口会假设你要创建一个必须返回给用户空间的虚拟文件,而这个文件是由一个项目序列贯穿而成的。为了使用seq_file,你必须创建一个简单的“迭代器”对象,此对象用来设定在序列中的位置。这听起来有些复杂,但实际上这个过程非常的简单。我们将通过scull驱动程序中一个/proc文件的完整建立过程来说明它的工作原理。
        The first step, inevitably, is the inclusion of . Then you must create four iterator methods, called start, next, stop, and show.
        第一步,肯定要包含头文件。然后你必须定义四个迭代器函数,分别为start,next,stop和show。
        The start method is always called first. The prototype for this function is:
       start函数总是第一个被调用。该函数原型如下:
void *start(struct seq_file *sfile, loff_t *pos);
   
    The sfile argument can almost always be ignored. pos is an integer
position indicating where the reading should start. The interpretation
of the position is entirely up to the implementation; it need not be a
byte position in the resulting file. Since seq_file implementations
typically step through a sequence of interesting items, the position is
often interpreted as a cursor pointing to the next item in the
sequence. The scull driver interprets each device as one item in the
sequence, so the incoming pos is simply an index into the scull_devices
array. Thus, the start method used in scull is:
        参数sfile大多数情况下会被忽略。参数pos是一个整型数值,它指明了读取操作的起始位置。具体的位置要在函数执行期间才能确定;它不需要结果文件中的字节位置。sfile参数几乎可以完全被忽略。pos是一个整数用来指明从哪个位置开始读取。具体的位置要在函数执行期间才能确定;它不需要结果文件中的字节位置。通常,当seq_file遍历了一个相关的项目序列后,其位置常常像一个指针而指向序列的下一个项目。scull驱动程序将其每一个设备都作为序列中的一员,所以为pos引入的值仅仅是scull_devices数组的一个索引号。因此,scull的start函数应该像下面这样:
static void *scull_seq_start(struct seq_file *s, loff_t *pos)
{
    if (*pos >= scull_nr_devs)
        return NULL;
    /* No more to read */
    return scull_devices + *pos;
}
        The return value, if non-NULL, is a private value that can be used by the iterator implementation.
        该函数的非NULL返回值是一个仅能被其他迭代器函数使用的值。
      
The next function should move the iterator to the next position,
returning NULL if there is nothing left in the sequence. This method's
prototype is:
        next函数应该把迭代器移动到下一个位置,如果返回NULL则表示已到达序列尾。该函数原型为:
void *next(struct seq_file *sfile, void *v, loff_t *pos);
   
    Here, v is the iterator as returned from the previous call to start
or next, and pos is the current position in the file. next should
increment the value pointed to by pos; depending on how your iterator
works, you might (though probably won't) want to increment pos by more
than one. Here's what scull does:
        在这里,参数v指的是从前一个start或next调用中返回的迭代器对象,参数pos是文件的当前读取位置。next应该对pos指向的数值进行累加;这取决于你的迭代器的工作方式,你可能(或者不必要)需要对pos进行多次累加。下面就是scull的next函数:
static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    (*pos)++;
    if (*pos >= scull_nr_devs)
        return NULL;
    return scull_devices + *pos;
}
    When the kernel is done with the iterator, it calls stop to clean up:
    当内核处理完迭代器后,它会调用stop函数来进行清除操作:
void stop(struct seq_file *sfile, void *v);
        The scull implementation has no cleanup work to do, so its stop method is empty.
        因为scull没有清除工作可做,所以它的stop函数是空的。
      
It is worth noting that the seq_file code, by design, does not sleep or
perform other nonatomic tasks between the calls to start and stop. You
are also guaranteed to see one stop call sometime shortly after a call
to start. Therefore, it is safe for your start method to acquire
semaphores or spinlocks. As long as your other seq_file methods are
atomic, the whole sequence of calls is atomic. (If this paragraph does
not make sense to you, come back to it after you've read the next
chapter.)
        由于seq_file的设计初衷,请不要在调用start函数和stop函数之间放置有关睡眠操作或者执行其他无原子任务的代码。你有时也肯定会看到在调用start函数后立刻就调用stop的情况。因此,它并不会影响你的start函数获取信号或自旋锁。只要你的其他seq_file函数是有原子的,那么整个函数序列就是有原子的。(如果你没明白这段话的意义,那么在你读完下一章时再回来温习它。)
      
In between these calls, the kernel calls the show method to actually
output something interesting to the user space. This method's prototype
is:
        在调用上两个函数之间,内核会调用show函数来向用户空间实际输出一些信息。该函数的原型如下:
int show(struct seq_file *sfile, void *v);
   
    This method should create output for the item in the sequence
indicated by the iterator v. It should not use printk, however;
instead, there is a special set of functions for seq_file output:
        参数v指向的迭代器对象指明了一个项目序列,该函数应该为这个序列中的项目建立输出信息。但是在此函数中不应该使用printk,取而代之的是一个专用于seq_file输出的函数集合:
int seq_printf(struct seq_file *sfile, const char *fmt, ...);
   
    This is the printf equivalent for seq_file implementations; it
takes the usual format string and additional value arguments. You must
also pass it the seq_file structure given to the show function,
however. If seq_printf returns a nonzero value, it means that the
buffer has filled, and output is being discarded. Most implementations
ignore the return value, however.
        这个函数对seq_file来说相当于printf函数;它需要通常格式的字符串及一些其他值作为参数。你还必须将传递给show函数的seq_file结构指针传给该函数。如果seq_printf返回一个非0值,则意味着缓冲区已被填满,输出信息就会被丢掉。然而,大多数情况下都会忽略这个返回值。
int seq_putc(struct seq_file *sfile, char c);
int seq_puts(struct seq_file *sfile, const char *s);
These are the equivalents of the user-space putc and puts functions.
它们相当于用户空间中的putc和puts函数。
int seq_escape(struct seq_file *m, const char *s, const char *esc);
   
    This function is equivalent to seq_puts with the exception that any
character in s that is also found in esc is printed in octal format. A
common value for esc is " \t\n\\", which keeps embedded white space
from messing up the output and possibly confusing shell scripts.
        该函数与seq_puts相比增加了一个esc参数,该参数的作用是将参数s中的任何字符以八进制形式输出。esc通常包含“\t\n\\”,这是为了防止输出信息中包含的空格搞乱shell脚本。
int seq_path(struct seq_file *sfile, struct vfsmount *m, struct dentry *dentry, char *esc);
   
    This function can be used for outputting the file name associated
with a given directory entry. It is unlikely to be useful in device
drivers; we have included it here for completeness.
        
        该函数能够输出与一个指定目录项有关的文件名称。它不太可能用于设备驱动;我们只是为了全面性而提到它。
Getting back to our example; the show method used in scull is:
再回到我们的示例程序中;下面是scull使用的show函数:
static int scull_seq_show(struct seq_file *s, void *v)
{
    struct scull_dev *dev = (struct scull_dev *) v;
    struct scull_qset *d;
    int i;
    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;
   
seq_printf(s, "\nDevice %i: qset %i, q %i, sz %li\n", (int) (dev -
scull_devices), dev->qset, dev->quantum, dev->size);
    for (d = dev->data; d; d = d->next)
    {
        /* scan the list */
        seq_printf(s, " item at %p, qset at %p\n", d, d->data);
        if (d->data && !d->next) /* dump only the last item */
        for (i = 0; i qset; i++)
        {
            if (d->data) seq_printf(s, " % 4i: %8p\n", i, d->data);
        }
    }
     up(&dev->sem);
    return 0;
}
        Here, we finally interpret our "iterator" value, which is simply a pointer to a scull_dev structure.
        在这里,我们最终指定了我们的“迭代器”数值,它其实就是一个简单指向某个scull_dev结构的指针。
   
    Now that it has a full set of iterator operations, scull must
package them up and connect them to a file in /proc. The first step is
done by filling in a seq_operations structure:
        现在已经有了一组完整的iterator操作函数,然后scull需要将它们封装并连接到/proc目录下的某个文件。这里首先要做的是填充一个seq_operations结构:
static struct seq_operations scull_seq_ops =
{
     .start = scull_seq_start,
    .next = scull_seq_next,
    .stop = scull_seq_stop,
    .show = scull_seq_show
};
   
    With that structure in place, we must create a file implementation
that the kernel understands. We do not use the read_proc method
described previously; when using seq_file, it is best to connect in to
/proc at a slightly lower level. That means creating a file_operations
structure (yes, the same structure used for char drivers) implementing
all of the operations needed by the kernel to handle reads and seeks on
the file. Fortunately, this task is straightforward. The first step is
to create an open method that connects the file to the seq_file
operations:
        然后,把这个结构放在合适的地方,我们必须要创建一个内核能理解的文件处理函数。我们不能使用前面提到的read_proc函数;当使用seq_file时,最好在一个较低的级别上连接到/proc目录。这是为了建立一个file_operations结构(是的,与字符驱动程序中的那个一样),该结构要能实现内核读取及查找文件所需的所有功能。幸运的是,这个任务非常明确。第一步就是创建一个open函数把文件和seq_file操作关联到一起:
static int scull_proc_open(struct inode *inode, struct file *file)
{
        return seq_open(file, &scull_seq_ops);
}
   
    The call to seq_open connects the file structure with our sequence
operations defined above. As it turns out, open is the only file
operation we must implement ourselves, so we can now set up our
file_operations structure:
        调用seq_open函数按照上面序列函数定义的顺序连接file结构。最后,只有open函数需要我们自己来实现,所以现在我们就可以建立我们的file_operations结构体了:
static struct file_operations scull_proc_ops =
{
    .owner = THIS_MODULE,
    .open = scull_proc_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release
};
   
    Here we specify our own open method, but use the canned methods
seq_read, seq_lseek, and seq_release for everything else.
        在这里我们指定了自己的open函数,但是其他的都用了现有的seq_read,seq_lseek和seq_release函数。
The final step is to create the actual file in /proc:
最后就是在/proc目录下创建实际的文件:
entry = create_proc_entry("scullseq", 0, NULL);
if (entry)
    entry->proc_fops = &scull_proc_ops;
        Rather than using create_proc_read_entry, we call the lower-level create_proc_entry, which has this prototype:
        这里我们使用比create_proc_read_entry更好的低级别函数create_proc_entry,该函数原型如下:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);
   
    The arguments are the same as their equivalents in
create_proc_read_entry: the name of the file, its protections, and the
parent directory.
        此函数的参数与create_proc_read_entry相同:文件名称,文件保护掩码以及文件父目录。
      
With the above code, scull has a new /proc entry that looks much like
the previous one. It is superior, however, because it works regardless
of how large its output becomes, it handles seeks properly, and it is
generally easier to read and maintain. We recommend the use of seq_file
for the implementation of files that contain more than a very small
number of lines of output.
        通过上面这些代码,scull有了一个新的/proc条目,这个新条目看起来很像原来的那个。然而它却更加高级,因为它不必去在意它的输出量有多大,它可以正确地处理查找操作,并且更易于读取和维护。所以,我们推荐你用seq_file
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/52272/showart_1773534.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP