Kernel之seq_file接口
Kernel之seq_file接口
背景
最近在看/proc/cpuinfo
的内核实现时,发现了一个以前没注意的东东: seq_file
接口
seq_file(Sequence file:序列文件)接口
内核文档: Documentation/filesystems/seq_file.txt
There are numerous ways for a device driver (or other kernel component) to
provide information to the user or system administrator. One useful
technique is the creation of virtual files, in debugfs, /proc or elsewhere.
Virtual files can provide human-readable output that is easy to get at
without any special utility programs; they can also make life easier for
script writers. It is not surprising that the use of virtual files has
grown over the years.Creating those files correctly has always been a bit of a challenge,
however. It is not that hard to make a virtual file which returns a
string. But life gets trickier if the output is long - anything greater
than an application is likely to read in a single operation. Handling
multiple reads (and seeks) requires careful attention to the reader’s
position within the virtual file - that position is, likely as not, in the
middle of a line of output. The kernel has traditionally had a number of
implementations that got this wrong.
主要是由于proc
等虚拟文件系统在处理大一点的数据时有比较大的局限性,很容易出错导致bug,所以seq_file
接口就出来了,它提供了更加友好的接口,来方便程序员操作
简单例子
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#define VERSION "0.1"
#define MODULE_NAME ""
//#define USE_SEQ_OPEN
#define TEST_NODE_NAME "seq_test"
static int c_show(struct seq_file *m, void *v)
{
seq_puts(m, "test start...\n");
seq_printf(m, "test: %d\n", 0);
seq_puts(m, "\n");
return 0;
}
#ifdef USE_SEQ_OPEN
static void *c_start(struct seq_file *m, loff_t *pos)
{
return *pos < 1 ? (void *)1 : NULL;
}
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
++*pos;
return NULL;
}
static void c_stop(struct seq_file *m, void *v)
{
}
const struct seq_operations seq_ops = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = c_show
};
static int test_open(struct inode *inode, struct file *file)
{
printk("test_open: seq_open()\n");
return seq_open(file, &seq_ops);
}
#else
static int test_open(struct inode *inode, struct file *file)
{
printk("test_open: single_open()\n");
return single_open(file, c_show, inode->i_private);
}
#endif
static ssize_t test_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
printk("test_write: count=%d\n",count);
return count;
}
static const struct file_operations proc_seq_test_ops = {
.open = test_open,
.read = seq_read,
.write = test_write,
.llseek = seq_lseek,
#ifdef USE_SEQ_OPEN
.release = seq_release,
#else
.release = single_release,
#endif
};
/*模块加载函数,通过insmod命令加载模块时,被自动执行*/
static int __init seq_test_init(void)
{
printk("init module... \n");
proc_create(TEST_NODE_NAME, 0, NULL, &proc_seq_test_ops);
return 0;
}
/*模块卸载函数,当通过rmmod命令卸载时,会被自动执行*/
static void __exit seq_test_exit(void)
{
printk("exit modules... \n");
remove_proc_entry(TEST_NODE_NAME, NULL);
}
module_init(seq_test_init); /*声明初始化函数*/
module_exit(seq_test_exit); /*声明卸载函数*/
MODULE_AUTHOR("zdd <xxx@xxx.cn>"); /*模块作者,可选*/
MODULE_LICENSE("GPL"); /*模块许可,描述内核模块的许可权限,必须*/
MODULE_VERSION(VERSION); /*模块版本,可选*/
参考
- Kernel内核文档及源码
- linux内核seq_file接口