分享

proc_read

 WUCANADA 2012-06-14
在linux的源码里/fs/proc/generic.c中找到这么一段:
* How to be a proc read function
* ------------------------------
* Prototype:
* int f(char *buffer, char **start, off_t offset,
* int count, int *peof, void *dat)
*
* Assume that the buffer is "count" bytes in size.
*
* If you know you have supplied all the data you
* have, set *peof.
*
* You have three ways to return data:
* 0) Leave *start = NULL. (This is the default.)
* Put the data of the requested offset at that
* offset within the buffer. Return the number (n)
* of bytes there are from the beginning of the
* buffer up to the last byte of data. If the
* number of supplied bytes (= n - offset) is
* greater than zero and you didn't signal eof
* and the reader is prepared to take more data //这句话是什么鸟文啊?哪有这么种格式的啊?理解不了
* you will be called again with the requested //我的翻译:如果n-offset大于0 ,没有标志eof,并且准备读取更多数据

* offset advanced by the number of bytes
// 那么,你将被再次请求用指定的offset来读取要求读取的count个数据 * absorbed. This interface is useful for files
* no larger than the buffer.
* 1) Set *start = an unsigned long value less than
* the buffer address but greater than zero.
* Put the data of the requested offset at the
* beginning of the buffer. Return the number of
* bytes of data placed there. If this number is
* greater than zero and you didn't signal eof
* and the reader is prepared to take more data
* you will be called again with the requested //这里还是不大明白
* offset advanced by *start. This interface is
* useful when you have a large file consisting
* of a series of blocks which you want to count
* and return as wholes.
* (Hack by Paul.Russell@rustcorp.com.au)
* 2) Set *start = an address within the buffer.
* Put the data of the requested offset at *start. //这个怎么实现的呢?
* Return the number of bytes of data placed there.
* If this number is greater than zero and you
* didn't signal eof and the reader is prepared to
* take more data you will be called again with the
* requested offset advanced by the number of bytes
* absorbed.

作者: deepwhite 发布日期: 2010-12-03
大概看了一下:

int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);

page 是内核提供给驱动的内存页(大小由 PAGE_SIZE 决定),用于驱动向其中写入信息;

start 也是内核提供给驱动的,用于提示驱动,从前面提供的 Page 的哪个部分开始写入数据。而后面的 offset 则是用户空间传给内核的参数,用于告诉内核从 proc 里面的文件的哪个偏移开始读取数据。换句话说, offset 说明了从待读取的文件的哪个位置开始“读 ”, 而 start 说明了驱动应该从内核提供的 Page 的那个位置开始 “写 ”。 后面的 generic.c 的注释中,解释了 start 的几个可能的值及其含义,我没有仔细看。至于 start 为什么是 char ** , generic.c 调用 proc_read 之前,使用 char *page 表示了 page 的地址,这里需要向 read_read() 中传入的是一个 地址的指针,当然是 (char **) 了,这样做的目的,可能是为了使驱动在向 page 写入数据的过程中有权利来修改 *start 的值。

作者: wucongdonglai 发布日期: 2010-12-03


我理解错了, offset 说明了读取位置的偏移,但是 start 应该是驱动返回给内核的信息。
具体细节我再看看。
作者: deepwhite 发布日期: 2010-12-03
1.
QUOTE:
* Leave *start = NULL. (This is the default.)
* Put the data of the requested offset at that
* offset within the buffer. Return the number (n)
//这里就不明白,在缓存区的偏移量处存放要求偏移的数据 ,这requested offset 和that offset应该不是同一个,而且不是要把缓存区内容放到内存页里么,怎么成这意思了?

两 个 offset 就是同一个,中文意思是说,把从 offset 位置取得的数据,放到 buff 的 offset 位置去。但,这里你把 buff 和 page 看成了两个东西,但实际上, page 这个词是 LDD 里面提到的,而在这个文章的注释中提到的 buff , 实际上就是 page ,他们是一个东西。

注意,我说的是注释中,而不是代码中。

举例来说,正常情况下,第一次读取数据的时候, offset 为0, 那么,读取到的数据也应该放到内核为驱动提供的空间的 0 位置上。

2.
QUOTE:
* of bytes there are from the beginning of the
//返回从缓存区开始到结束的字节数n ,这不是已经假定为缓存区字节为count了么
* buffer up to the last byte of data. If the

buff 的长度是 count, 但是对文件的“读取”未必会一次就能够把文件的内容都读出来。所以这里的 n ,个人觉得应该是在这一次读取中读到的数据的长度,这个长度和 buff 大小显然不是一个东西。


3.
QUOTE:
* number of supplied bytes (= n - offset) is
* greater than zero and you didn't signal eof
* and the reader is prepared to take more data
* you will be called again with the requested
//这you后面开始那句我想来想去;还是不明白意思啊
* offset advanced by the number of bytes
* absorbed.

但是linux设备驱动程序上说:
如果保留*start为空,内核将假定数据保存在内存页偏移量0的地方;也就是说,内核将对read_proc作如下假定:
该函数将虚拟文件的整个内容放到了内存页,并同时忽略offset参数。
从这上来说,*start为空时,也就是把缓存区里的count个字节内容直接放到内存页的开始处。这和上面那段英文的做法好像不同啊

这段注释的目的,是向驱动开发者来展示内核处理 read_proc 返回结果的原则和流程,以便驱动开发人员根据这个原则来填写自己的上 read_proc() 。

*start == NULL 的时候, read_proc() 返回的数据量较小,应该可以放在一个 Page 内。这种情况下,应该可以一次读取过程就能够将所有的数据都返回给用户空间 (读取完成后 EOF 被设置),此时 offset 看起来就象是没什么用一样。

而如果驱动在 read_proc () 中,没有设置 EOF, 此时 offset 就有用了。
因为 EOF 没有设置,说明文件的读取还没有结束,内核还会再次调用 read_proc() , 并且从新的 offset (也就是原来的 offset 与已经读取的数据长度之和) 开始读数,并将其拷贝到用户空间。


LDD 中说的是比较简单的情况, 而内核代码中则很完善。两者没有冲突。
这里的关键在于所谓 char **buffer_location 是个逻辑值,不是个物理上有意义的值,它的意义只对你自己有用,
内核不知道它是什么意思,内核所做的事情只是在发现你还没*eof=1之前就反复调用你的xxx_proc_read,
每次调用,给你传下来:新的offset = 老的offset+你上次传上去的*buffer_location

举个例子,假设你有8K数据,打算以256Byte为单位往上传,它的代码大致如下
define TOTAL_BLOCK_NR = 8*1024/256;

struct my_data_struct
{
unsigned char[256];
};
struct my_data_struct my_buf[TOTAL_BLOCK_NR];
static ssize_t xxx_proc_read(char *buffer,
char **buffer_location,
off_t offset, int buffer_length, int *eof,
void *data)
{
if (offset >= TOTAL_BLOCK_NR) {
*eof = 1;
return 0;
}

int i = offset;
ssize_t cur_size = 0;
for (i=offset;i<TOTAL_BLOCK_NR;i++)
{
if (cur_size+256>buffer_length)
{
break;
}
memcpy(buffer, my_buf[i], 256);
buffer += 256;
cur_size+=256;
}

//我这一次传了多少组?告诉内核,让内核帮我记着,下次通过@offset告诉我
*buffer_location = ( i - offset);
return (i-offset)*256; //我这一次实际传了多少字节

}




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多