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

memfd_create匿名文件被select误判可读的问题解决问询

问题根源与解决方案

为什么memfd会让select一直标记可读

memfd创建的匿名文件属于常规文件类型,而POSIX标准中select对常规文件的可读就绪判定逻辑是:只要文件当前的读写指针没有到达EOF,就会一直认为该文件描述符可读——哪怕没有新的内容写入。这就是你的select始终被唤醒的核心原因,常规文件并不适合用来做事件触发的通知源。

正确的替代方案

要实现“写入时唤醒select”的事件通知,应该使用专门设计的IPC/事件通知机制:

  • 管道(pipe):最经典的跨线程/进程事件通知方式,读端仅在有数据写入时才会被select标记为可读,读取数据后恢复未就绪状态。
  • eventfd:Linux特有的轻量级事件通知fd,比管道更高效,仅用于传递事件计数,同样符合select的触发规则。

管道实现示例代码

替换原来的memfd创建逻辑,改用管道:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>

int main() {
    int notify_pipe[2];
    // 创建管道
    if (pipe(notify_pipe) == -1) {
        perror("pipe failed");
        exit(EXIT_FAILURE);
    }

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(notify_pipe[0], &read_fds);
    int max_fd = notify_pipe[0];

    // 模拟异步写入(实际场景可能是其他线程/进程)
    pid_t pid = fork();
    if (pid == 0) {
        sleep(2);
        char buf[] = "wake up";
        write(notify_pipe[1], buf, sizeof(buf));
        close(notify_pipe[1]);
        exit(EXIT_SUCCESS);
    }

    printf("Waiting for event...\n");
    int ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
    if (ret == -1) {
        perror("select failed");
        exit(EXIT_FAILURE);
    }

    if (FD_ISSET(notify_pipe[0], &read_fds)) {
        char buf[1024];
        ssize_t n = read(notify_pipe[0], buf, sizeof(buf));
        printf("Got event, read %zd bytes: %s\n", n, buf);
    }

    close(notify_pipe[0]);
    return 0;
}

eventfd实现示例代码

如果用更轻量的eventfd:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/eventfd.h>
#include <inttypes.h>

int main() {
    int efd = eventfd(0, EFD_NONBLOCK);
    if (efd == -1) {
        perror("eventfd failed");
        exit(EXIT_FAILURE);
    }

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(efd, &read_fds);
    int max_fd = efd;

    pid_t pid = fork();
    if (pid == 0) {
        sleep(2);
        eventfd_write(efd, 1); // 写入事件计数
        close(efd);
        exit(EXIT_SUCCESS);
    }

    printf("Waiting for event...\n");
    int ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
    if (ret == -1) {
        perror("select failed");
        exit(EXIT_FAILURE);
    }

    if (FD_ISSET(efd, &read_fds)) {
        uint64_t value;
        ssize_t n = eventfd_read(efd, &value);
        printf("Got event, count: %"PRIu64"\n", value);
    }

    close(efd);
    return 0;
}

运行效果说明

以上两种实现中,select会在写入操作完成后才被唤醒,读取数据/事件计数后,fd会恢复为未就绪状态,下次只有新的写入操作才会再次触发select。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 01:07:31