You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何字符设备驱动收到的读取长度远超系统调用指定的1字节?

字符设备驱动读取长度不匹配问题解析

问题背景

我正在测试一款可输出单个字符的字符设备,使用如下C++程序进行测试:

int main() // c++ program
{
    while (true)
    {   
        char tmp = 'x';
        std::ifstream ms_fd;
        ms_fd.open(device_path, std::ios::binary);
        ms_fd.read(&tmp, 1);
        std::cout << "val: " << tmp << std::endl;

        ms_fd.read(&tmp, 1);
        assert(ms_fd.eof());
        ms_fd.close();
        sleep(2);
    }
    return 0;
}

代码中每次调用read请求读取1字节,但设备驱动的read函数收到的读取长度却为8191。

驱动日志信息如下:

[  204.738324] READDRIVE:  reading len: [8191]  offset: [0]
[  204.738590] READDRIVE:  reading len: [8191]  offset: [1]
[  206.739116] READDRIVE:  reading len: [8191]  offset: [0]
[  206.739235] READDRIVE:  reading len: [8191]  offset: [1]

驱动read函数实现:

static ssize_t pir_ms_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
    pr_info("READDRIVE:  reading len: [%d]  offset: [%d]\n", len, *off);
    ssize_t retval;
    if (*off > 0)
    {
        retval = 0;
    }
    else
    {
        put_user(buf_state, buf);
        retval = 1;
        *off+=1;
    }
    return retval;
}

为何驱动未接收到指定的1字节读取请求?

核心原因

问题出在C++标准库的std::ifstream默认行为上:它会启用用户态缓冲区优化,不会直接将代码中指定的1字节读取请求传递给内核的read系统调用。

具体流程:

  • 当你调用ms_fd.read(&tmp, 1)时,ifstream会主动向内核发起一个批量读取请求(默认缓冲区大小通常为8191字节),将数据读取到自己维护的用户态缓冲区中。
  • 之后ifstream再从自身缓冲区中取出1字节返回给你的代码,因此内核驱动收到的是缓冲区大小级别的读取请求,而非你指定的1字节。

解决方案

要让读取请求精确传递给驱动,需要绕过ifstream的缓冲区机制,有两种可行方式:

1. 禁用ifstream的缓冲区

在打开设备文件后,添加以下代码关闭缓冲区同步并禁用流缓冲区:

ms_fd.open(device_path, std::ios::binary);
ms_fd.sync_with_stdio(false); // 关闭与C标准IO的同步
ms_fd.rdbuf()->pubsetbuf(nullptr, 0); // 禁用流自身的缓冲区

2. 直接使用系统调用read

绕过C++标准库封装,直接调用Linux系统级read函数,确保请求参数直接传递给驱动:

#include <fcntl.h>
#include <unistd.h>

int main()
{
    while (true)
    {
        char tmp = 'x';
        int fd = open(device_path, O_RDONLY | O_BINARY);
        read(fd, &tmp, 1);
        std::cout << "val: " << tmp << std::endl;

        ssize_t ret = read(fd, &tmp, 1);
        assert(ret == 0); // 此时应该返回EOF(0)
        close(fd);
        sleep(2);
    }
    return 0;
}

驱动代码优化建议

你的驱动read函数存在两个潜在问题:

  • 格式符使用错误:size_t和loff_t属于宽度不确定的类型,不能用%d打印,应替换为:
    pr_info("READDRIVE:  reading len: [%zu]  offset: [%lld]\n", len, *off);
    
  • EOF处理逻辑:当*off > 0时返回0符合EOF规范,但如果设备是单次打开仅能读取一个字符,当前逻辑是合理的,无需修改。

内容的提问来源于stack exchange,提问作者shafe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:14:53