Linux驱动面试经常追问:“应用程序在/dev下打开设备文件后,read()到底执行了什么?”如果只回答“进驱动的read函数”,既不准确也无法解释阻塞、缓存和设备实际传输之间的关系。
从上到下看调用链
应用程序调用libc封装的read(),通常通过系统调用进入内核,内核依据文件描述符找到打开文件对象,再经VFS和对应文件操作将请求交给相关子系统或驱动。对于某些字符设备,驱动可实现file_operations里的read回调;但并非所有文件类型都使用同一条具体路径。图中分层关系是宏观示意,不是Linux源码调用栈。

阻塞和非阻塞差在哪
阻塞:没有数据可读时,驱动可能让线程进入等待队列,直到数据到达、信号打断或出现错误;是否等待由设备语义和实现决定。
非阻塞:启用O_NONBLOCK后,当前确实无数据可读时,常见返回-1且errno为EAGAIN或EWOULDBLOCK。
返回值:read()返回的是成功读取的字节数;零通常意味着到达文件尾或特定设备语义下无更多数据,不应一概解释成“硬件死了”。
最容易被追问的三个问题
copy_to_user为什么必要?用户指针和内核地址空间的访问规则不同,驱动需要按内核提供的安全拷贝接口处理用户缓冲区。
read是不是必须触发一次硬件总线传输?不一定。驱动可能从环形队列或已缓存的数据读取,设备数据也可能由中断、DMA或其他机制预先采集。
poll/epoll能替代read吗?不能。它们主要帮助等待就绪事件,真正读取数据仍要调用合适的IO操作。
回答时的主线
“我会先讲清文件描述符、VFS及驱动操作的关系,再区分可读就绪和真实读取。设计驱动时,我还会处理用户缓冲区拷贝、并发访问、等待唤醒和设备拔出等异常路径。”

扫码关注






























