Chinaunix首页 | 论坛 | 博客
  • 博客访问: 51789
  • 博文数量: 19
  • 博客积分: 930
  • 博客等级: 准尉
  • 技术积分: 160
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-29 17:58
文章分类
文章存档

2009年(8)

2008年(11)

我的朋友
最近访客

分类: LINUX

2009-01-04 19:06:45

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.

        第一步,肯定要包含头文件然后你必须定义四个迭代器函数,分别为startnextstopshow


        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数组的一个索引号。因此,scullstart函数应该像下面这样:

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进行多次累加。下面就是scullnext函数:

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 < dev->qset; i++)
        {
            if (d->data[i]) seq_printf(s, " % 4i: %8p\n", i, d->data[i]);
        }
    }
     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_readseq_lseekseq_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
阅读(1015) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~