Linux读取/proc文件时proc_read()为何被重复调用直至返回0?
问题背景
你在《操作系统概念》中实现的是Linux内核的/proc文件系统模块,你给出的模块代码如下:
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #define BUFFER_SIZE 128 #define PROC_NAME "hello" ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos); static struct file_operations proc_ops = { .owner = THIS_MODULE, .read = proc_read, }; /* 模块加载时调用该函数 */ int proc_init(void) { /* 创建/proc/hello入口 */ proc_create(PROC_NAME, 0666, NULL, &proc_ops); return 0; } /* 模块卸载时调用该函数 */ void proc_exit(void) { /* 删除/proc/hello入口 */ remove_proc_entry(PROC_NAME, NULL); }
你遇到的疑问是执行cat /proc/hello时,proc_read()会被重复调用直到返回0的原因,以及该函数的调用方,以下是具体解答:
proc_read()的调用方 proc_read()是你注册到proc文件系统的自定义读回调函数,触发和调用链路如下:
- 用户空间执行
cat /proc/hello命令时,cat工具会主动发起read()系统调用,请求读取目标文件的内容 - 内核的虚拟文件系统(VFS)层接收到read系统调用后,会根据读取的文件所属的文件系统类型(这里是procfs),匹配到你创建/proc/hello节点时注册的
proc_ops操作结构体,最终调用你实现的proc_read()函数处理读请求
本质上该函数的调用方是内核的VFS层,属于内核态的回调函数,由用户态的读操作触发。
重复调用直到返回0的原因
这是Linux系统中read()操作的通用约定,分为内核和用户态两层逻辑:
- 内核回调返回值规则
VFS层要求所有文件的读回调返回值遵循统一规则:返回正整数表示本次调用成功读取的字节数;返回0表示已经读到文件末尾(EOF),没有更多内容可以返回;返回负整数表示读操作出现错误。
同时proc_read()的入参loff_t *pos是当前读操作的偏移量,每次回调成功返回正整数后,VFS会自动更新pos的数值,下一次回调时pos会指向尚未读取的内容起始位置。
- 用户态读工具的逻辑
你用到的cat工具的默认读逻辑是:只要read系统调用返回值大于0,就认为还有内容没读完,会持续发起read系统调用;只有返回0(读到EOF)或者返回负数(出错)时,才会停止读操作。
举个实际的实现例子,如果你要在读取/proc/hello时返回"Hello World\n"共12字节,proc_read()的正确实现逻辑如下:
ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos) { char buf[] = "Hello World\n"; int len = sizeof(buf); // 如果偏移量已经大于等于内容长度,说明已经读完,返回0(EOF) if (*pos >= len) { return 0; } // 把内核空间的内容拷贝到用户空间缓冲区 if (copy_to_user(usr_buf, buf, len)) { return -EFAULT; } // 更新偏移量 *pos += len; // 返回本次读取的字节数 return len; }
如果你的proc_read()实现永远返回正整数,就会触发cat无限发起读请求,出现死循环。
内容的提问来源于stack exchange,提问作者Kaiyu Yang
相关产品推荐
相关产品推荐

