首先,可以肯定的是proc文件系统是 支持大文件读写的。通常情况下,我们都是先通过函数create_proc_entry()创建一个文件记录,然后将自己实现的读函数read_proc、写函数write_proc和文件相关数据data赋值给刚才新建的文件记录。如下所示[ 1]:
|
其中read_proc和write_proc的原型分别如下:
|
其参数分别表示:
page: 因为proc文件系统读操作实现的是:在读的时候首先向系统申请一个内存页,然后将这个内存页的起始地址作为第一个参数(page)调用上面的函数,接着再将由上面的函数写入page里面的内容复制到用户空间的缓冲区,所以 不需要校验由page和count所标示的地址是否是合理的用户空间地址,实际上校验是无论如何也通过不了的,因为它切切实实地是内核空间的地址。
start: 这个参数有些特殊,LDD3(Linux Device Driver 3rd edition)称其的出现和用法为hack。start的用法也确实有些诡异,读过内核里面的相关注释和源码后先总结如下:
- 忽视start参数或把start赋值为空(NULL):这时的proc是不支持大于一个内存页大小的数据传输到用户空间的,通常和它一并忽略的还有off和count参数,这个时候内核程序员还需明白page所指的内存空间为一个内存页,应该将全部内容直接写到其中,否则off不为0的read调用可能得到错误的数据或得不到数据。
- 将start赋值为比page的地址值小的数:这种情况下,off所代表的意义将和start重合,可以表示记录的索引号,而不是确切的字节偏移,甚至可以是程序员自己定义的任意值。
- 将start赋值为由page所标示的内存页所在的地址空间中的一个地址值:这个时候off表示字节偏移,start表示返回的缓冲区的起始地址,通常直接赋值为page。
off: 可以表示多种意图,具体参见上面对参数start的解释。
count: 表示这次调用用户期待的数据量,一般小于页大小。可以被忽略。
eof: 将*eof置为`1'表示已经读到了文件的结尾,换句话说这可能是最后一次返回数据,以后再也没有多余的数据可以返回。如果你忽略了count,那么也请你同时忽略它。
data: proc文件记录中的数据成员data的值。
返回值: 统一表示写到page所标示的缓冲区的字节数。
怎么样,由start所引发的三种工作模式,确实够让人头晕的吧?如果感觉我说的还是不甚明白(我都感觉自己在说绕口令),建议自己去阅读内核的源码,那才是程序员间相互交流的语言。
|
write_proc_t就要比read_proc_t简洁许多了,参数意义也简单明了, file就是这个proc文件所对应的文件结构, buffer和 count标示 用户空间的缓冲区(需要谨慎对待), data和read_proc_t中的data同义,都是表示proc文件记录中的数据成员data的值。你可能已经发现它的参数中没有off选项了,不错,它确实不区分对同一个文件连续调用的write,所以为了避免数据截断,你有以下三种选择:
- 不用这个接口,转用更加底层的file_operation接口中的write[3]。
- 一次性将所有的数据通过write系统调用写入proc文件。
- 自己在data所指的自定义结构中保存偏移信息,不过这个时候你还需要保证对data所指数据操作的原子性。
|
我想这就勿需再解释什么了。
看到这里,我想你也该同意我刚才所说的话了吧?回想一下自己以前的代码中是否有上面提到的细节问题,如果有赶紧去改正...
注意:
proc文件的get_info()接口本文并未提及,原因是太古老了。
参考资料:
[1] 在 Linux 下用户空间与内核空间数据交换的方式:procfs
[2] 在 Linux 下用户空间与内核空间数据交换的方式:seq_fs
[3] Manage /proc file with standard filesystem