内核 seq_file 操作函数

接口API

seq_file 系列函数是为了方便内核导出信息到 sysfsdebugfsprocfs 实现的。以往的内核也存在各种形式的实现,但是都无法避免个别实现会产生一些漏洞,这一套函数实现可以让内核导出信息更加简单和统一稳定,维护起来更加方便。

它包含了如下一些接口:

int seq_open(struct file *file, const struct seq_operations *op);
int seq_release(struct inode *inode, struct file *file);
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos);
int seq_write(struct seq_file *seq, const void *data, size_t len);
loff_t seq_lseek(struct file *file, loff_t offset, int whence)

如何实现这些函数呢?举个例子,一般我们需要创建一个 file_operations 结构体去实现不同的 debugfsprocfs。而以上函数就是用于快速实现这个操作结构体的。比如:

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

seq_open 操作

本节将介绍打开操作,主要功能是创建并初始化 seq_file 结构体,设置 seq_operations 操作回调。如下图所示:
在这里插入图片描述

  • seq_operations 结构体
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);
};

该结构体是一系列回调函数,用于 seq_file 系列操作内部使用。

seq_read 操作

seq_read 操作是系列函数中最为重要的一个函数,它负责从内部buffer缓冲区中读取数据并 copy_to_user 返回给应用层。阅读代码会发现该函数实现如下操作:

  • 创建buffer,默认大小为1个page
  • 调用seq_operations中的 start() 函数
  • 调用seq_operations中的 show() 函数,填充数据到buffer中
  • 调用 next() 函数,检查是否需要继续写入,如果有则循环执行上面的步骤
  • 判断buffer大小是否够用,如果不够,则释放buffer,重新申请一个更大的buffer(前面的2倍),用于数据写入
  • 循环执行以上步骤,如果不用继续写入,并且buffer没有溢出,那么执行 copy_to_user()拷贝buffer中的数据到用户空间

所以对于read操作,最关键的是实现那一系列的 seq_operations 回调函数,不用维护缓冲区。

seq_write 操作

seq_write 是用于写入数据到内部的buffer缓冲区使用的函数,但是千万不要被它的名字所迷惑,这个操作并不是从应用层接收写入数据的。按照 seq_read 的逆过程来理解,它应该从应用层接收数据并写入到内部buffer才对。但实际并非如此,这个函数实现极其简单,它只负责写入数据到内部buffer,该函数不涉及内存分配,buffer是由 seq_read 在读取时分配的内存,因此该函数一般在实现 show() 函数中使用,比如:在 show 函数中把驱动数据通过 seq_write 写入到buffer中。

这样数据写入操作的调用栈为: seq_read --> show --> seq_write

seq_printf 操作

seq_write 类似, seq_printf 函数主要区别在于格式化输出数据到buffer中,使用的场景也与 seq_write 一样,一般在实现 show 函数中使用。

single_open 操作

为了更进一步简化内核接口的实现,封装了该函数,这样我们不用实现整个 seq_operations 结构体,驱动开发者只需要传入 show 即可,其他回调都使用内核实现的一个简单版本,它的代码如下所示:

int single_open(struct file *file, int (*show)(struct seq_file *, void *),
        void *data)
{
    struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL_ACCOUNT);
    int res = -ENOMEM;

    if (op) {
        op->start = single_start;
        op->next = single_next;
        op->stop = single_stop;
        op->show = show;
        res = seq_open(file, op);
        if (!res)
            ((struct seq_file *)file->private_data)->private = data;
        else
            kfree(op);
    }
    return res;
}

seqfile的优点

简化了文件操作的实现,内部维护缓冲区,内部维护缓冲区的当前位置,不用驱动开发者自己实现缓冲区操作,对于驱动开发人员只需要实现相关的数据操作函数回调即可。在 procfssysfsdebugfs 接口中被广泛的应用。


欢迎扫码关注我的公众号!
在这里插入图片描述

  • 9
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值