LDD: ch4补充,seq_file的介绍

总结自linux下的seq_file.txt的document。

虚拟文件系统如procfs,debugfs等在建立虚拟文件的时候,都要设计到虚拟文件的操作。
使用虚拟文件的时候比较挑战的一点在于,当虚拟文件相当大的时候,操作虚拟文件需要一些技巧,在多次读写的时候要小心的操作文件的position,当虚拟文件的使用越来越广泛的时候,这样的操作代码也越来越多,因此在2.6内核中加入了一组函数来方便实现虚拟文件的操作,这就是seq_file。


1. seq_file

seq_file声明在

1.1 seq_file的iterator接口

希望使用seq_file接口的模块都必须要要实现iterator对象来遍历虚拟文件的数据。
iterator接口包含4个函数需要实现用于遍历:

struct seq_operations {
    void * (*start) (struct seq_file *m, loff_t *pos);
    void (*stop) (struct seq_file *m, void *v);
    void * (*next) (struct seq_file *m, void *v, loff_t *pos);
    int (*show) (struct seq_file *m, void *v);
};
1.1.1 start接口

start接口接受一个pos作为起始位置,并且返回一个iterator指针。对于一个顺序访问的虚拟文件来说,最简单的start函数可能如下:

static void *ct_seq_start(struct seq_file *s, loff_t *pos)
{
        loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL);
        if (! spos)
                return NULL;
        *spos = *pos;
        return spos;
}

对于顺序访问的文件来说,只需要一个loff_t来记录虚拟文件的当前访问位置即可,这个loff_t就是iterator对象。

对于复杂的应用场景,seq_file中的private可以用来记录信息。
当start的pos为0的时候,start函数可以有一个特殊的返回值SEQ_START_TOKEN,这个值从来让show接口了解到当前正在起始位置从而输出header信息。

1.1.2 next接口

next接口用于将虚拟文件位置移动pos个位置,参数v就是start接口中返回的iterator。
next接口会返回更新后的iterator,或者在没有数据的时候返回NULL。

static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
        loff_t *spos = v;
        *pos = ++*spos;
        return spos;
}
1.1.3 stop接口

stop接口是做iterator对象的cleanup动作,主要就是释放在start中申请的iterator对象。

static void ct_seq_stop(struct seq_file *s, void *v)
{
        kfree(v);
}
1.1.4 show接口

show函数,将iterator指向的位置的对象进行格式化输出。

static int ct_seq_show(struct seq_file *s, void *v)
{
        loff_t *spos = v;
        seq_printf(s, "%lld\n", (long long)*spos);
        return 0;
}

show函数正常都应该返回0,负数是表明发生了某些错误。
show函数可以返回SEQ_SKIP,表明当前的item会被skip,如果之前show已经产生过输出,那么这整个输出都会被丢弃掉。

1.1.5 小结

seq_file在设计的时候,start()和stop()之间是不会sleep的,所以在start和stop之间持有一个锁是很合理的,除此之外,不会持有其他的锁。

1.2 格式化输出

seq_file的主要功能就是管理虚拟文件的位置,最终将输出传递给用户用户。内核中提供了一组函数,用来帮助输出内容。

int seq_printf(struct seq_file *m, const char *f, ...)

最常用的输出函数是seq_printf,函数和printk类似,输出内容保存在seq_file的buffer中。

1.2.2 直接的字符输出
int seq_putc(struct seq_file *m, char c);
int seq_puts(struct seq_file *m, const char *s);
int seq_escape(struct seq_file *m, const char *s, const char *esc);

seq_putc和seq_puts很简单,就是将一个字符/字符串输入到seq_file中去。
seq_escape是在seq_puts的基础上,将esc字符串中所包含的字符都转为用8进制的显示。

1.3 file_operation的实现

在虚拟文件中使用seq_file,需要实现一组file_operation,seq_file也提供给了一组可用的函数。

int seq_open(struct file *file, const struct seq_operations *op);

seq_open中会使用seq_operations建立seq_file对象,并将其赋值给到file->private_data。seq_open常用来实现open操作,下面就是一个最简单的示例:

static int ct_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &ct_seq_ops);
}

对于read,lseek,release操作,seq_file中已经提供直接可用的函数:

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos);
loff_t seq_lseek(struct file *file, loff_t offset, int whence);
int seq_release(struct inode *inode, struct file *file);

简单的例子如下:

static const struct file_operations ct_file_ops = {
        .owner   = THIS_MODULE,
        .open    = ct_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
        .release = seq_release
};

1.3.2 single_open()和single_release()

对于极端简单的例子,可以使用一个早期的接口single_open,

int single_open(struct file *file, int (*show)(struct seq_file *, void *),
        void *data)

single_open完成的工作和seq_open类似,只是不需要编写完整的seq_operation,只需要实现其中的show接口即可,其他接口会有默认的实现
和single_open配套的release函数是single_release

int single_release(struct inode *inode, struct file *file);

2. seq_list

如果虚拟文件被组织成list的形式,那么下面的seq_list的函数会比较有用,方便实现对应的seq_operation操作。

struct list_head *seq_list_start(struct list_head *head,
                     loff_t pos);
struct list_head *seq_list_start_head(struct list_head *head,
                      loff_t pos);
struct list_head *seq_list_next(void *v, struct list_head *head,
                    loff_t *ppos);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值