您的位置:首页 > 运维架构 > Linux

linux的机制和策略通信—seqfile

2010-10-12 16:19 267 查看
linux的机制和策略通信—seqfile

seqfile不是一个独立的文件系统,它在某种意义上就是一个数据格式化系统,用它的意义就是可以平滑地从内核得到数据。因为原始的procfs的
read例程只能读取最大一个页面的数据,大于一个页面的数据就要在用户空间重复读,因此需要一个机制,在内核空间可以连续不断的将数据取出,而不管数据
有多大。

struct seq_file {

char *buf;

size_t size;

size_t from;

size_t count;

loff_t index;

struct semaphore sem;

struct seq_operations *op;

void *private;

};


很多linux的特性一样,seqfile也有相关的操作,linux善于将操作封装成一个XX_operations的结构体,那么对于seqfile
机制,这个操作例程集就是seq_operations了,策略正是在这些回调例程要提供的,seqfile提供了一套将数据格式化的机制,数据不再像传
统的那样,靠独有的read例程最终内部调用copy_to_user拷贝给用户,如此一来,拷贝数据的大小和类型就受到了此独有的read例程的限制,
比如procfs的file_operations实现的read函数就限制了数据最大为1个页面,seqfile机制不是这样的,它做到具体如何拷贝由
内核模块编写实现,写得很糟糕的可能最后一字节也没有拷贝,写得好的可以一次拷贝好几G的数据,就是这样,用户告知内核怎么拷贝。

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_operations里面的回调函数就是定位/格式化的作用了,start返回第一次的位置,相当于拿到了事情,next返回下一步的位置,相当
于一步一步的做事,而show就是格式化操作了,它按照用户的实现格式化了数据。

在seqfile机制中,因为它并不是一个独立的文件系统,
故而没有必要提供一个完整的file_operations结构体,而是仅仅提供一个seq_operations就可以了,但是为了使得seqfile
机制更加完整,linux内核还是提供了一些松散的read,llseek等函数,这些函数并不像别的read,llseek等函数属于一个
file_operations,它们是游离的,因此你可以将它们用到任何file_operations中,也就是替换掉任何
file_operations的read等函数,那么原先的read函数的逻辑怎么实现呢?如果你真的这么做了,我承认你是高手,首先第一步就是进行抽
象,将原先的read逻辑抽象成一步一步的事情,然后把每一步如何定位偏移实现在next回调函数里面,注意next返回的是个指针,它可以是任何类型,
不一定是文件偏移位置,只要你可以合理并相同地解释next的返回值和next的第二个参数就可以了,最后在每一步的show中,将数据按照原先的意思进
行格式化,格式化成的数据放进一个buff里面,最后拷贝给用户。我觉得这么做挺好,并且希望将来有一天,所有的file_operations中的函数
全部用类似的机制实现,这样抽象程度更高,编程的门槛更低,十分不错,我指的仅仅是用类似的机制而不是特定就是seqfile。下面就看一下最常被用到的
seq_read吧:

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

{

struct seq_file *m = (struct seq_file *)file->private_data;

size_t copied = 0;

loff_t pos;

size_t n;

void *p;

int err = 0;

down(&m->sem);

/* grab buffer if we didn't have one */

if (!m->buf) {
//读的时候还没有分配空间,这可以吗?注意,seqfile和linux中的别的特性一样只提供机制,那么这里的m->buf根本就是一个二传
手,为了格式化数据而存在的临时缓存,具体过程就是必须有相关措施将内核中的真正数据拷贝到m->buf中,然后再把m->buf的内容拷贝
到用户空间,具体如何将真正的内核数据拷贝给m->buf和江m->buf拷贝给用户空间就是seqfile机制的事情了,其实就是
seq_read的逻辑.

m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);

if (!m->buf)

goto Enomem;

}

if (m->count) { //首先读出剩余的数据

n = min(m->count, size);

err = copy_to_user(buf, m->buf + m->from, n);

if (err)

goto Efault;

m->count -= n;

m->from += n;

size -= n;

buf += n;

copied += n;

if (!m->count)

m->index++;

if (!size)

goto Done;

}

while (1) { //这个循环并不是真正的拷贝数据,而是“试探”需要空间的大小,注意每次增加一个页面的大小的整数倍

pos = m->index;

p = m->op->start(m, &pos);

err = PTR_ERR(p);

if (!p || IS_ERR(p))

break;

err = m->op->show(m, p);

if (err)

break;

if (m->count < m->size) //如果当前buf大小够用的话就直接跳转到填充。

goto Fill;

m->op->stop(m, p);

kfree(m->buf);

m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); //增加buf的大小,每次一个页面的整数倍,并且二倍增长

if (!m->buf)

goto Enomem;

m->count = 0;

}

m->op->stop(m, p);

m->count = 0;

goto Done;

Fill:

while (m->count < size) {
//循环读取数据,这个循环怎么没有看到更新m->count的,注意,这个值是在show中被更改的,因为一切逻辑都在start/next
/show/stop中,那么外界也就没有必要干预其“内政”了

size_t offs = m->count;

loff_t next = pos;

p = m->op->next(m, p, &next); //得到下一次要做的事情的抽象实体。

...//太长了,故而错误判断省略

err = m->op->show(m, p); //格式化

...//推出条件省略

pos = next;

}

m->op->stop(m, p); //结束,善后。

n = min(m->count, size);

err = copy_to_user(buf, m->buf, n); //拷贝给用户空间。

...//省略一些必要的处理,对于讲述seqfile逻辑思想没有什么用。

}

show
回调函数主要负责格式化数据,怎么格式化当然是用户的策略了,seqfile机制并不干预,但是当你知道了怎么格式化以后,真正格式化的还是内
核,seqfile的机制可以帮你格式化数据,同时数据当前位置值也随之更新。可以看出,linux内核十分清楚机制和策略的边界在那里,一点也不为用户
做不该做的事,当然该做的事,内核一件也不少做。

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

{

va_list args;

int len;

if (m->count < m->size) {

va_start(args, f);

len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);

va_end(args);

if (m->count + len < m->size) {

m->count += len;

return 0;

}

}

m->count = m->size;

return -1;

}


到最后,我想总结一下了,linux中有很多的规范,这都不算什么,最要紧的是,linux允许人们动态注册根据该规范机制实现的策略模块,这就是
linux中著名的“确定规范--注册使用”的特性,比如文件系统,netfilter,sysctl,驱动等等好多好多都是。

转自:http://blog.csdn.net/dog250/archive/2010/02/09/5303437.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: