Linux内核通信seq_file详解

seq_file适用于内核需要向应用层输出信息时使用,最常见的用法是遍历内核中的一个list数据结构输出list的内容到应用层;当然也可以输出任意的数据,并且输出到应用层的数据大小没有限制,默认缓冲区是一个PAGE_SIZE,当输出的数据大于PAGE_SIZEseq_file会把缓冲区大小翻倍,直到超过要输出的数据大小,或者把内存耗尽。

seq_file不能单独使用,需要配合procfs或者sysfs等文件系统使用,利用文件系统提供的file_operations接口和应用层交互;seq_file本身也无法接收来自应用层的数据,同样需要使用file_operations提供的接口接收应用层写入的数据。

下面以procfs为例介绍seq_file的用法

注: seq_开头的函数是seq_file提供的接口, my_开头的函数是我们自己实现的函数

procfs接口

首先, 我们需要创建一个proc文件, 以便用户可以通过该文件read内核的数据, 这里我们需要使用seq_file提供的接口填充file_operations结构, 以使用seq_file的能力, 不过open函数需要我们自己实现, 下文将提供示例

static const struct file_operations my_proc_ops = {
	.open      = my_seq_open, // 我们自己实现
	.read      = seq_read, // seq_file提供
	.llseek    = seq_lseek, // seq_file提供
	.release   = seq_release, // seq_file提供
};
// 调用proc_create在procfs中创建文件,以便应用层可以`read`内核数据
proc_create("/proc/my_seq", 0644, NULL, &my_proc_ops)

seq_file接口

这里我们来实现procfs接口中file_operations用到的my_seq_open函数, 并在该函数中初始化seq_file系统. my_seq_open的实现需要用到一个seq_operations结构, 此结构用于对seq_file的操作, 在应用层调用read函数读取/proc/my_seq时, seq_file会调用seq_operations结构中的相关函数指针从内核读取数据, 这些函数指针正是使用seq_file的关键

static const struct seq_operations my_seq_ops = {
	.start	= my_seq_ops_start,
	.next	= my_seq_ops_next,
	.stop	= my_seq_ops_stop,
	.show	= my_seq_ops_show,
};

int my_seq_open(struct inode *inode, struct file *file)
{
    seq_open(file, my_seq_ops)
}

下面我们以遍历输出内核中的task为例, 来实现seq_operations中的函数指针


// 开始输出任务列表
// my_seq_start()的返回值,会传递给my_seq_next()的v参数
static void *my_seq_start(struct seq_file *m, loff_t *pos)
{
    loff_t index = *pos;
    struct task_struct *task;

    klogi(log_tag "my_seq_start m=%pK, pos=%d, index=%lld", m, (int)(*pos), m->index);
    
    // 如果缓冲区不足, seq_file可能会重新调用start()函数, 
    // 并且传入的pos是之前已经遍历到的位置,
    // 这里需要根据pos重新计算开始的位置
    for_each_process(task){
        if (index-- == 0){
            return task;
        }
    }

    return NULL;
}
// 继续遍历, 直到my_seq_next()放回NULL或者错误
static void *my_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
    struct task_struct *task = next_task((struct task_struct *)v);

    klogi(log_tag "my_seq_next m=%pK, v=%pK, pos=%d, index=%lld, comm=[%s]", m, v, (int)*pos, m->index, task->comm);

    // 这里加不加好像都没有作用
    ++*pos;

    // 返回NULL, 遍历结束
    if(task == &init_task){
        return NULL;
    }
    return task;
}
// 遍历完成/出错时seq_file会调用stop()函数
static void my_seq_stop(struct seq_file *m, void *v)
{
    klogi(log_tag "my_seq_stop m=%pK, v=%pK, index=%lld", m, v, m->index);
}
// 此函数将数据写入`seq_file`内部的缓冲区
// `seq_file`会在合适的时候把缓冲区的数据拷贝到应用层
// 参数@V是start/next函数的返回值
static int my_seq_show(struct seq_file *m, void *v)
{
    struct task_struct * task = (struct task_struct *)v;

    klogi(log_tag "my_seq_show m=%pK, v=%pK, index=%lld comm=[%s]", m, v, m->index, task->comm);
    
    seq_printf(m, "PID=%u, task: %s, index=%lld, read_pos=%lld\n", task->tgid, task->comm, m->index, m->read_pos);

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值