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

memfd_create创建的内存fd调用read无数据不阻塞如何解决

问题背景

此前尝试创建行为与字符设备完全一致的内存文件,最初调用memfd_create(path, 0)实现,经测试判断该接口无法满足需求。
核心需求为:通过LD_PRELOAD机制hook open函数,当检测到程序调用open("/dev/input/event0")时,将返回的文件描述符替换为自定义内存fd;后续实现数据队列供被hook的应用读取,要求读取行为与真实设备文件完全一致。
测试中遇到的问题:对memfd_create返回的fd调用read()时,即使当前无可读字节,read也不会阻塞,需要正确的实现方向指引。
附测试代码:

struct input_event ev;
int kbd = memfd_create("/dev/input/event0", 0);
read(kbd, &ev, sizeof(ev)); //<-- 如何让该read调用默认阻塞?
问题根因

memfd_create创建的是匿名内存支持的普通临时文件,属于普通文件类型,VFS层对普通文件的read操作天生没有阻塞等待逻辑:只要文件当前偏移指向末尾无数据可读,read会直接返回0,不会等待新数据写入。这是普通文件和字符设备的核心语义差异,无论怎么修改fd的文件状态标记都无法改变这个底层行为,因此memfd从设计上就不匹配这个场景的需求。

可行实现方案
  • 优先选择:用UNIX域套接字对/匿名管道替代memfd
    这两类IPC对象返回的fd天生符合阻塞读语义:当接收缓冲区无数据时,read会自动阻塞,直到有新数据写入、对端关闭才会返回,和字符设备的读行为基本一致。
    实现逻辑很简单:hook到open("/dev/input/event0")调用时,调用socketpair(AF_UNIX, SOCK_STREAM, 0, fds)或者pipe(fds)创建一对连通的fd,将读端返回给被hook的应用,写端留在hook逻辑中,后续生成input_event数据时直接往写端写入即可。
    适配注意点:真实的/dev/input/event0是input字符设备,除了read之外还支持一系列专属ioctl命令(比如EVIOCGVERSION、EVIOCGBIT、EVIOCGNAME等),同时支持poll/select/epoll事件通知,你需要同步hook这些系统调用,补全对应语义,才能做到和真实设备行为完全一致。
    改造后的示例代码如下:
    #include <sys/socket.h>
    #include <linux/input.h>
    #include <unistd.h>
    
    int main() {
        struct input_event ev;
        int fds[2];
        // 创建字节流套接字对
        socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
        int kbd = fds[0]; // 读端返回给业务应用
        // 无数据时该read调用会默认阻塞,符合字符设备行为
        read(kbd, &ev, sizeof(ev));
        close(kbd);
        close(fds[1]);
        return 0;
    }
    
  • 备选方案1:基于FUSE实现用户态文件系统
    如果需要更精细的语义控制,可以通过FUSE编写一个极简的用户态文件系统,自己实现read、poll、ioctl、release等所有文件操作回调,完全匹配真实input设备的行为,缺点是需要挂载FUSE文件系统,部署步骤比LD_PRELOAD+socketpair的方案复杂。
  • 备选方案2:内核态misc字符设备
    自行编写内核模块注册一个misc字符设备,在内核层实现数据队列和阻塞逻辑,行为和真实设备完全一致,但需要root权限加载内核模块,适配成本最高,不适合轻量hook场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 03:24:28