关于proc文件系统对大文件支持的若干问题

本文深入探讨了Proc文件系统的编程接口,特别是read_proc和write_proc函数的使用细节,包括如何正确处理大文件读写,以及不同参数配置下的工作模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

proc文件系统凭借其编程接口的“简单易学”,和很多书籍和参考资料都把它放在较为靠前的位置(很多人看书都不能坚持到底),成为了那些需要快速开发的私有内核程序的“首选”内核空间和用户空间通信接口,被广泛地应用于实际编程中。但是我们中大多都忽视了其“简单”中由于种种细节所造成的“复杂”性,尤其在用它传输大量数据的时候。本文将努力详尽地分析它的细节,给大家一个参考。

首先,可以肯定的是proc文件系统是 支持大文件读写的。通常情况下,我们都是先通过函数create_proc_entry()创建一个文件记录,然后将自己实现的读函数read_proc、写函数write_proc和文件相关数据data赋值给刚才新建的文件记录。如下所示[ 1]:

        entry = create_proc_entry("aint", 0644, myprocroot);
        if (entry) {
                entry->data = &int_var;
                entry->read_proc = &int_read_proc;
                entry->write_proc = &int_write_proc;
        }


其中read_proc和write_proc的原型分别如下:

typedef int (read_proc_t)(char *page, char **start, off_t off, int count, int *eof, void *data);


其参数分别表示:

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。
在上面三种用法中,第二种曾经比较常用,也比较好用,但是hack的方法所带来的不一致严重影响了人们对它的理解,所以现在的内核鼓励用更好的seq_file API[ 2]向用户空间输出面向记录的数据。

off: 可以表示多种意图,具体参见上面对参数start的解释。

count: 表示这次调用用户期待的数据量,一般小于页大小。可以被忽略。

eof: 将*eof置为`1'表示已经读到了文件的结尾,换句话说这可能是最后一次返回数据,以后再也没有多余的数据可以返回。如果你忽略了count,那么也请你同时忽略它。

data: proc文件记录中的数据成员data的值。

返回值: 统一表示写到page所标示的缓冲区的字节数。

怎么样,由start所引发的三种工作模式,确实够让人头晕的吧?如果感觉我说的还是不甚明白(我都感觉自己在说绕口令),建议自己去阅读内核的源码,那才是程序员间相互交流的语言。

typedef int (write_proc_t)(struct file *file, const char __user *buffer, unsigned long count, void *data);


write_proc_t就要比read_proc_t简洁许多了,参数意义也简单明了, file就是这个proc文件所对应的文件结构, buffercount标示 用户空间的缓冲区(需要谨慎对待), data和read_proc_t中的data同义,都是表示proc文件记录中的数据成员data的值。你可能已经发现它的参数中没有off选项了,不错,它确实不区分对同一个文件连续调用的write,所以为了避免数据截断,你有以下三种选择:
  • 不用这个接口,转用更加底层的file_operation接口中的write[3]。
  • 一次性将所有的数据通过write系统调用写入proc文件。
  • 自己在data所指的自定义结构中保存偏移信息,不过这个时候你还需要保证对data所指数据操作的原子性。
也许你也会想到直接操纵file结构中的off_set属性,不过很遗憾,此路不通。原因请看内核中的下列代码:

        loff_t pos = file_pos_read(file);
        ret = vfs_write(file, buf, count, &pos);
        file_pos_write(file, pos);


我想这就勿需再解释什么了。

看到这里,我想你也该同意我刚才所说的话了吧?回想一下自己以前的代码中是否有上面提到的细节问题,如果有赶紧去改正...

注意

proc文件的get_info()接口本文并未提及,原因是太古老了。

参考资料:

[1] 在 Linux 下用户空间与内核空间数据交换的方式:procfs
[2] 在 Linux 下用户空间与内核空间数据交换的方式:seq_fs
[3] Manage /proc file with standard filesystem
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值