linux seq_file 机制

和device_attr proc什么区别呢?seq_file 是 proc 的改进版本,device_attr 也可以认为是 proc 的改进吧

在老版本的Linux内核中,proc文件系统有一个缺陷:如果输出内容大于1个内存页(4096 Bytes),需要多次读,因此处理起来很难。另外,如果输出内容太大,速度会比较慢。在2.6内核中,由于大量使用了seq_file功能,使得内核输出大文件信息更容易。使用seq_file需要包含头文件linux/seq_file.h,并定义一个seq_operations结构:

struct seq_operations {  
        void * (*start) (struct seq_file *m, loff_t *pos);// 指定seq_file文件的读开始位置  
        void (*stop) (struct seq_file *m, void *v);//关闭seq_file  
        // 把seq_file文件的当前读位置移动到下一个读位置  
        void * (*next) (struct seq_file *m, void *v, loff_t *pos);  
        int (*show) (struct seq_file *m, void *v);//格式化输出  
};  

seq_file的常用操作接口如下:

int seq_open(struct file *, const struct seq_operations *);//打开  
ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);//读  
loff_t seq_lseek(struct file *, loff_t, int);//定位  
int seq_release(struct inode *, struct file *);//释放  
int seq_escape(struct seq_file *, const char *, const char *);//写缓冲,忽略某些字符  
int seq_putc(struct seq_file *m, char c);// 把一个字符输出到seq_file文件  
int seq_puts(struct seq_file *m, const char *s);// 把一个字符串输出到seq_file文件  

下面以cpuinfo为例说明seq_file在proc中的使用。

void create_seq_entry(char *name, mode_t mode, const struct file_operations *f)  
{  
    struct proc_dir_entry *entry;  
    entry = create_proc_entry(name, mode, NULL);  // create_proc_entry 函数已经被 proc_create 取代
    if (entry)  
        entry->proc_fops = f;  
}  
void __init proc_misc_init(void)  
{  
    …  
    create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations); //创建/proc目录  
}  

定义proc_cpuinfo_operations:

static const struct file_operations proc_cpuinfo_operations = {  
    .open   = cpuinfo_open,  
    .read       = seq_read,  
    .llseek = seq_lseek,  
    .release    = seq_release,  
};  

接下来看cpuinfo_open:

static int cpuinfo_open(struct inode *inode, struct file *file)  
{  
    return seq_open(file, &cpuinfo_op);  
}  

cpuinfo_op是与硬件平台相关的。ARM的cpuinfo_op在/arc/arm/kernel/setup.c中:

struct seq_operations cpuinfo_op = {  
    .start  = c_start,  
    .next   = c_next,  
    .stop   = c_stop,  
    .show= c_show 
};  

cpuinfo_op就是实际上对/proc/ cpuinfo进行修改的操作接口,其中最重要的是c_show:

static int c_show(struct seq_file *m, void *v)  
{  
    int i;  
    seq_printf(m, "Processor\t: %s rev %d (%s)\n",  
           cpu_name, (int)processor_id & 15, elf_platform);  
#if defined(CONFIG_SMP)//针对多处理器  
    for_each_online_cpu(i) {  
        seq_printf(m, "processor\t: %d\n", i);  
        seq_printf(m, "BogoMIPS\t: %lu.%02lu\n\n",  
               per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ),  
               (per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100);  
    }  
#else /* CONFIG_SMP */  
    seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",  
           loops_per_jiffy / (500000/HZ),  
           (loops_per_jiffy / (5000/HZ)) % 100);  
#endif  
    seq_puts(m, "Features\t: ");  
    for (i = 0; hwcap_str[i]; i++)  
        if (elf_hwcap & (1 << i))  
            seq_printf(m, "%s ", hwcap_str[i]);  
    seq_printf(m, "\nCPU implementer\t: 0x%02x\n", processor_id >> 24);  
    seq_printf(m, "CPU architecture: %s\n", proc_arch[cpu_architecture()]);  
    if ((processor_id & 0x0008f000) == 0x00000000) {/* pre-ARM7 */  
        seq_printf(m, "CPU part\t: %07x\n", processor_id >> 4);  
    } else {  
        if ((processor_id & 0x0008f000) == 0x00007000) {/* ARM7 */  
            seq_printf(m, "CPU variant\t: 0x%02x\n",  
                   (processor_id >> 16) & 127);  
        } else {/* ARM7以上的CPU */  
            seq_printf(m, "CPU variant\t: 0x%x\n",  
                   (processor_id >> 20) & 15);  
        }  
        seq_printf(m, "CPU part\t: 0x%03x\n",(processor_id >> 4) & 0xfff);  
    }  
    seq_printf(m, "CPU revision\t: %d\n", processor_id & 15);  
    {  
        unsigned int cache_info = read_cpuid(CPUID_CACHETYPE);  
        if (cache_info != processor_id) {  
            seq_printf(m, "Cache type\t: %s\n"  
                      "Cache clean\t: %s\n"  
                      "Cache lockdown\t: %s\n"  
                      "Cache format\t: %s\n",  
                   cache_types[CACHE_TYPE(cache_info)],  
                   cache_clean[CACHE_TYPE(cache_info)],  
                   cache_lockdown[CACHE_TYPE(cache_info)],  
                   CACHE_S(cache_info) ? "Harvard" : "Unified");  
            if (CACHE_S(cache_info)) {  
                c_show_cache(m, "I", CACHE_ISIZE(cache_info));  
                c_show_cache(m, "D", CACHE_DSIZE(cache_info));  
            } else {  
                c_show_cache(m, "Cache", CACHE_ISIZE(cache_info));  
            }  
        }  
    }  
    seq_puts(m, "\n");  
    seq_printf(m, "Hardware\t: %s\n", machine_name);  
    seq_printf(m, "Revision\t: %04x\n", system_rev);  
    seq_printf(m, "Serial\t\t: %08x%08x\n",system_serial_high, system_serial_low);  
    return 0;  
}  
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)
{
}

参考文章:http://blog.csdn.net/luckywang1103/article/details/50635327  里面有很好的例子

头文件linux/seq_file.h 
seq相关函数的实现在fs/seq_file.c

struct seq_file {
        char *buf;
        size_t size;
        size_t from;
        size_t count;
        loff_t index;
        loff_t read_pos;
        u64 version;
        struct mutex lock;
        const struct seq_operations *op;
        int poll_event;
        void *private;
};
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);
};

start实现初始化工作,在遍历一个链接对象开始时调用 
stop当所有链接对象遍历结束时调用,主要完成一些清理工作 
next用来在遍历中寻找下一个链接对象,返回下一个对象或者NULL 
show对遍历对象进行操作的函数主要是调用seq_printf, seq_puts之类的函数,打印出这个对象节点的信息。

//seq操作包括以下一系列函数
int seq_open(struct file *, const struct seq_operations *);
ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);
loff_t seq_lseek(struct file *, loff_t, int);
int seq_release(struct inode *, struct file *);

实现例子

/*
 * Documentation/filesystem/seq_file.txt
 */
 #include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

#define DEBUG_SEQ

#ifdef DEBUG_SEQ
#define log_seq(...) printk(__VA_ARGS__)
#else
#define log_seq(...)
#endif

static void *ct_seq_start(struct seq_file *s, loff_t *pos)
{
        int *count = s->private;

        log_seq("%s\n", __func__);

        if ((long long)*pos < *count) {
                printk("start pos %lld\n", (long long)*pos);
                return pos;
        }

        return NULL;
}

static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
        int *count = s->private;

        log_seq("%s\n", __func__);

        ++*pos;
        if ((long long)*pos < *count) {
                printk("next pos %lld\n", (long long)*pos);
                return pos;
        }

        return NULL;
}

static void ct_seq_stop(struct seq_file *s, void *v)
{
        log_seq("%s\n", __func__);
}

static int ct_seq_show(struct seq_file *s, void *v)
{
        loff_t *pos = v;

        log_seq("%s\n", __func__);

        seq_printf(s, "%lld\n", (long long)*pos);
        return 0;
}

static const struct seq_operations ct_seq_ops = {
        .start = ct_seq_start,
        .next = ct_seq_next,
        .stop = ct_seq_stop,
        .show = ct_seq_show
};

static int ct_open(struct inode *inode, struct file *file)
{
        int ret;
        struct seq_file *s;

        ret = seq_open(file, &ct_seq_ops);

        s = file->private_data;
        s->private = (void *)kmalloc(sizeof(int), GFP_KERNEL);
        *((int *)s->private) = 5;

        return ret;
}

static int ct_close(struct inode *inode, struct file *file)
{
        struct seq_file *s = file->private_data;

        kfree(s->private);
        return seq_release(inode, file);
}

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

static int __init ct_init(void)
{
        struct proc_dir_entry *entry;

        entry = proc_create("sequence", 0, NULL, &ct_file_ops);
        return 0;
}

static void __exit ct_exit(void)
{
        remove_proc_entry("sequence", NULL);
}

module_init(ct_init);
module_exit(ct_exit);

155053_IQv6_592470.png

01-06 17:27:42.732     0     0 I         : ct_seq_start
01-06 17:27:42.734     0     0 I         : start pos 0
01-06 17:27:42.736     0     0 I         : ct_seq_show
01-06 17:27:42.738     0     0 I         : ct_seq_next
01-06 17:27:42.741     0     0 I         : next pos 1
01-06 17:27:42.743     0     0 I         : ct_seq_show
01-06 17:27:42.746     0     0 I         : ct_seq_next
01-06 17:27:42.748     0     0 I         : next pos 2
01-06 17:27:42.750     0     0 I         : ct_seq_show
01-06 17:27:42.753     0     0 I         : ct_seq_next
01-06 17:27:42.755     0     0 I         : next pos 3
01-06 17:27:42.758     0     0 I         : ct_seq_show
01-06 17:27:42.760     0     0 I         : ct_seq_next
01-06 17:27:42.762     0     0 I         : next pos 4
01-06 17:27:42.765     0     0 I         : ct_seq_show
01-06 17:27:42.767     0     0 I         : ct_seq_next
01-06 17:27:42.770     0     0 I         : ct_seq_stop
01-06 17:27:42.772     0     0 I         : ct_seq_start
01-06 17:27:42.775     0     0 I         : ct_seq_stop

整体看来,用户态调用一次读操作,seq_file流程为:该函数调用struct seq_operations结构提顺序为:start->show->next->show…->next->show->next->stop->start->stop来读取文件

 

 

 

转载于:https://my.oschina.net/u/592470/blog/1501528

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值