红联Linux门户
Linux帮助

/proc的读接口的问题

发布时间:2010-12-03 10:58:36来源:红联作者:wucongdonglai
《inux设备驱动程序》里有的调试技术这么一段关于/proc的:
要创建一个只读 /proc 文件, 你的驱动必须实现一个函数来在文件被读时产生数据. 当
某个进程读文件时(使用 read 系统调用), 这个请求通过这个函数到达你的模块. 我们先
看看这个函数并在本章后面讨论注册接口.
当一个进程读你的 /proc 文件, 内核分配了一页内存(就是说, PAGE_SIZE 字节), 驱动
可以写入数据来返回给用户空间. 那个缓存区传递给你的函数, 是一个称为 read_proc
的方法:
int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);
page 指针是你写你的数据的缓存区; start 是这个函数用来说有关的数据写在页中哪里
(下面更多关于这个); offset 和 count 对于 read 方法有同样的含义. eof 参数指向一
个整数, 必须由驱动设置来指示它不再有数据返回, data 是驱动特定的数据指针, 你可
以用做内部用途.

对于这个read_proc的函数的参数有点不解啊,网上查了查,讲的都比较粗略,所以想请教下大家:
当一个进程读你的 /proc 文件, 内核分配了一页内存
这是不是就是说在这一页内存上面存放的数据时需要读取的数据?
start 是这个函数用来说有关的数据写在页中哪里
而这里的start指针是不是指存放要读取数据的页中的位置?它的数据类型为什么是char **的?
[color=#ff0000]offset指的是不是循环缓冲区的偏移量,而不是这个分配的页的偏移量?[/color]
但是偏偏又有下面一段:
The start parameter has a somewhat unconventional use. Its purpose is to indicate
where (within page) the data to be returned to the user is found. When your proc_read
method is called, *start will be NULL. If you leave it NULL, the kernel assumes that the
data has been put into page as if offset were 0; in other words, it assumes a simpleminded
version of proc_read, which places the entire contents of the virtual file in page
without paying attention to the offset parameter. If, instead, you set *start to a non-
NULL value, the kernel assumes that the data pointed to by *start takes offset into
account and is ready to be returned directly to the user. In general, simple proc_read
methods that return tiny amounts of data just ignore start. More complex methods set
*start to page and only place data beginning at the requested offset there.

那显示指的是[color=#ff0000]offset分配的页的偏移量,但我就不明白了,既然已经有start 表明开始位置了,又搞个offset偏移量起什么作用呢?[/color]
文章评论

共有 7 条评论

  1. wucongdonglai 于 2010-12-06 09:25:39发表:

    6# deepwhite
    恩,差不多理解了,哈,谢谢white兄啊

  2. 依然眷顾 于 2010-12-06 06:32:06发表:

    向楼上的高手们学习~~

  3. deepwhite 于 2010-12-03 21:37:58发表:

    1.

    引用:

    * 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.
    引用:

    * 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.
    引用:

    * 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 中说的是比较简单的情况, 而内核代码中则很完善。两者没有冲突。

    你看看后面的代码会有所帮助。

  4. deepwhite 于 2010-12-03 18:53:23发表:

    我理解错了, offset 说明了读取位置的偏移,但是 start 应该是驱动返回给内核的信息。
    具体细节我再看看。

  5. wucongdonglai 于 2010-12-03 17:35:36发表:

    [i=s] 本帖最后由 wucongdonglai 于 2010-12-3 17:38 编辑 [/i]

    3# deepwhite
    white兄讲的还是很有道理的,先谢谢white兄的指点
    我当时是这样的,勉强对命令有所了解了,就去看深入理解linux内核这本书了,看到了第九章,实在是看不下去了,云里雾里的,所以就先搁着了!然后就开始看linux程序设计这边书,这本书讲的倒是比较浅而全的,算是把linux上的一些短板给补了补,当然里面也就遇到了一些C的问题!之后就开始现在的linux设备驱动程序了,我是这样想的:先学驱动,然后借此来学习linux内核,不然一开始学内核,有点太庞大了点,学起来太费劲了!所以说,现在就在学这驱动编程!英语方面倒真的是短板,以前还凑合,现在是越来越不行了,说来丢人!
    不过这段英文的确我看了N遍了,还是没看明白!这里帮忙看一看,解释解释啊!
    首先Assume that the buffer is "count" bytes in size. //这没什么说的
    然后看他的选项)
    * 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应该不是同一个,而且不是要把缓存区内容放到内存页里么,怎么成这意思了?
    * of bytes there are from the beginning of the
    //返回从缓存区开始到结束的字节数n ,这不是已经假定为缓存区字节为count了么
    * 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 [color=#444444]
    [/color]
    * you will be called again with the requested
    //这you后面开始那句我想来想去;还是不明白意思啊
    * offset advanced by the number of bytes
    * absorbed.
    [/color]
    [color=#ff0000][color=#444444]但是linux设备驱动程序上说:
    [/color]
    如果保留*start为空,内核将假定数据保存在内存页偏移量0的地方;也就是说,内核将对read_proc作如下假定:
    该函数将虚拟文件的整个内容放到了内存页,并同时忽略offset参数。
    从这上来说,*start为空时,也就是把缓存区里的count个字节内容直接放到内存页的开始处。这和上面那段英文的做法好像不同啊

  6. deepwhite 于 2010-12-03 16:16:00发表:

    大概看了一下:

    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 的值。


    那些被你称为 "鸟语" 的注释,个人觉得写的已经很清晰了,内核中能够出现这种程度的注释已经很不错了,看不懂的话,建议多看看内核吧。

    再多说一句,从最开始你在本版上发帖子到现在,我觉得你的进展有点过快,从开始基本的 C 语言问题,到后来咔嚓一下的多线程和内核问题,再到现在的驱动问题。我敢说你掌握的并不牢靠,很多东西别说是细节,可能连轮廓你都没有掌握。建议别一味追求速度,静下心仔细看看吧。以现在的这种状态去完成你的采集卡驱动,那个驱动估计会问题多多。

    上述言语,如果你觉得有道理就听听,觉得没有道理就忽略吧!

  7. wucongdonglai 于 2010-12-03 11:44:13发表:

    1# wucongdonglai
    在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 [color=seagreen]//这句话是什么鸟文啊?哪有这么种格式的啊?理解不了
    * you will be called again with the requested //我的翻译:如果n-offset大于0 ,没有标志eof,并且准备读取更多数据,[/color]
    * offset advanced by the number of bytes
    // 那么,你将被再次请求用指定的offset来读取要求读取的count个数据 * absorbed. [color=black]This interface is useful for files
    [/color] * 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 [email=Paul.Russell@rustcorp.com.au]Paul.Russell@rustcorp.com.au[/email])
    * 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.
    这里写的好生晦涩,看的我晕头转向,初步理解和linux设备驱动程序里讲的不一致啊