技术知识库
copy_to_user 成功但 cat 没有输出
copy_to_user 成功但 cat 没有输出
摘要
驱动的 read() 曾进入并执行 copy_to_user(),但 cat /dev/martin_mailbox 没有输出。根因是处理函数最后返回 0;对用户空间而言,0 表示 EOF,而不是“成功复制”。改为返回本次实际复制的 read_len,并在数据读完后才返回 0,写入与读取闭环随即通过。
问题/背景
实际操作:
echo "test" > /dev/martin_mailbox
cat /dev/martin_mailbox
早期版本只能从 dmesg 看到 martin_mailbox_read enter,终端没有 test。
根因
字符设备 read() 的返回值属于用户空间可见协议:
- 正数:本次成功读取的字节数。
0:EOF,没有更多数据。- 负数:错误码。
因此下面的结构有问题:
copy_to_user(buf, martin_mailbox_buf + *ppos, read_len);
*ppos += read_len;
return 0;
数据复制动作和函数返回协议是两件事。cat 看到 0 后按 EOF 处理,不能把这一轮当作读到数据。
最终修复
if (*ppos >= martin_mailbox_len)
return 0;
remaining = martin_mailbox_len - *ppos;
read_len = count;
if (read_len > remaining)
read_len = remaining;
if (copy_to_user(buf, martin_mailbox_buf + *ppos, read_len))
return -EFAULT;
*ppos += read_len;
return read_len;
第一次调用返回有效字节数;cat 再次调用时,偏移已达到数据长度,此时才返回 0 表示 EOF。
验证结果
root@rlk-arm64:/home/martin# echo "test" > /dev/martin_mailbox
root@rlk-arm64:/home/martin# cat /dev/martin_mailbox
test
故障闭环完成。
证据与来源
- Linux 5.0 ARM64 Guest 实际读写结果。
- 当前驱动源码:
/home/martin-zou/workspace/gcc-lab/char_mod/martin_mailbox/martin_mailbox.c。
关联
待验证
- 后续补充
llseek、阻塞读和多个进程共享文件位置时的行为。