V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
ksedz
V2EX  ›  程序员

原来还有可以 poll 不可以 epoll 的 fd

  •  
  •   ksedz · 2023-06-19 17:56:21 +08:00 · 1577 次点击
    这是一个创建于 557 天前的主题,其中的信息可能已经有所发展或是发生改变。
    int fd = open("/dev/urandom", O_RDONLY);
    

    加入 epoll 会报错 EPERM
    用 poll 可以正常得到结果

    求问什么原因


    用 select 也正常

    12 条回复    2023-06-20 17:24:28 +08:00
    pagxir
        1
    pagxir  
       2023-06-19 18:15:38 +08:00 via Android   ❤️ 1
    原因是这个设备驱动没有实现 epoll 的支持
    ksedz
        2
    ksedz  
    OP
       2023-06-19 18:18:56 +08:00
    @pagxir 完全知识盲区了,之前一直以为 epoll 是可以覆盖 select/poll 的功能的。谢谢解答,我补补知识点。
    codehz
        3
    codehz  
       2023-06-19 18:21:18 +08:00   ❤️ 1
    但是 urandom 按定义就是不会阻塞的,你这个 epoll 也没意义啊。。。
    urandom/random 只实现了 read_iter, write_iter, unlocked_ioctl, compat_ioctl, fasync, llseek, splice_read, splice_write 这几个方法,没有实现 poll 方法,因此 epoll 不能用(
    至于 poll 和 select 系统调用,当发现目标没有实现 poll 方法的时候,直接就原样放回去了(假装都可读写)
    ksedz
        4
    ksedz  
    OP
       2023-06-19 18:39:42 +08:00
    @codehz 我是跟 openssl 1.0.2 的时候跟踪到的,在 RAND_poll 中调用了对相应设备的 poll 。项目要求所有的 select / poll 都要转为 epoll 集中处理,就遇到了这个问题。
    查资料 /dev/urandom 在极端情况下会失败,那虽然它不阻塞,还是要去 poll/epoll 的。但如果设备驱动没有实现对应 poll 方法这就很尴尬了,只能直接在 hook 里让它调用成功了。谢谢讲解。
    codehz
        5
    codehz  
       2023-06-19 20:40:37 +08:00
    @ksedz urandom 极端情况也不会失效,那函数实现就是在没墒的时候发几个警告就过了
    static ssize_t urandom_read_iter(struct kiocb *kiocb, struct iov_iter *iter)
    {
    static int maxwarn = 10;

    /*
    * Opportunistically attempt to initialize the RNG on platforms that
    * have fast cycle counters, but don't (for now) require it to succeed.
    */
    if (!crng_ready())
    try_to_generate_entropy();

    if (!crng_ready()) {
    if (!ratelimit_disable && maxwarn <= 0)
    ++urandom_warning.missed;
    else if (ratelimit_disable || __ratelimit(&urandom_warning)) {
    --maxwarn;
    pr_notice("%s: uninitialized urandom read (%zu bytes read)\n",
    current->comm, iov_iter_count(iter));
    }
    }

    return get_random_bytes_user(iter);
    }
    可以看出根本没有失败的执行路径,get_random_bytes_user 里也没有任何失效的代码,就纯算法而已))出错就直接 panic 了,根本没机会返回爆炸的结果)
    你那个资料可能过时了))
    实际上按之前的 poll 方法,那也是纯粹毫无作用,是原开发者的错误理解,你这如果只需要考虑 linux 平台的话(你看都用 epoll 了,肯定是 linux only ),就直接返回可读即可
    neoblackcap
        6
    neoblackcap  
       2023-06-19 21:05:15 +08:00
    epoll 不是什么类型的 fd 都支持的,我记得文件文件 FD 就不支持而 kqueue(FreeBSD)则是支持。
    为了解决这个问题,所以又诞生了一个 inotify 。
    当然了 timerfd ,epoll 是支持的。
    ksedz
        7
    ksedz  
    OP
       2023-06-20 10:46:35 +08:00
    @codehz get_random_bytes_user 里还是可能失败的吧

    static ssize_t get_random_bytes_user(struct iov_iter *iter)
    {
    u32 chacha_state[CHACHA_STATE_WORDS];
    u8 block[CHACHA_BLOCK_SIZE];
    size_t ret = 0, copied;
    if (unlikely(!iov_iter_count(iter)))
    return 0;
    /*
    * Immediately overwrite the ChaCha key at index 4 with random
    * bytes, in case userspace causes copy_to_iter() below to sleep
    * forever, so that we still retain forward secrecy in that case.
    */
    crng_make_state(chacha_state, (u8 *)&chacha_state[4], CHACHA_KEY_SIZE);
    /*
    * However, if we're doing a read of len <= 32, we don't need to
    * use chacha_state after, so we can simply return those bytes to
    * the user directly.
    */
    if (iov_iter_count(iter) <= CHACHA_KEY_SIZE) {
    ret = copy_to_iter(&chacha_state[4], CHACHA_KEY_SIZE, iter);
    goto out_zero_chacha;
    }
    for (;;) {
    chacha20_block(chacha_state, block);
    if (unlikely(chacha_state[12] == 0))
    ++chacha_state[13];
    copied = copy_to_iter(block, sizeof(block), iter);
    ret += copied;
    if (!iov_iter_count(iter) || copied != sizeof(block))
    break;
    BUILD_BUG_ON(PAGE_SIZE % sizeof(block) != 0);
    if (ret % PAGE_SIZE == 0) {
    if (signal_pending(current))
    break;
    cond_resched();
    }
    }
    memzero_explicit(block, sizeof(block));
    out_zero_chacha:
    memzero_explicit(chacha_state, sizeof(chacha_state));
    return ret ? ret : -EFAULT;
    }


    满足 iov_iter_count(iter) <= CHACHA_KEY_SIZE 并且 copy_to_iter(&chacha_state[4], CHACHA_KEY_SIZE, iter) 返回 0
    codehz
        8
    codehz  
       2023-06-20 10:57:42 +08:00
    @ksedz 那是用户态提供 buffer 有问题的情况才会失败,这个角度所有设计 buffer 的 syscall 你都得考虑失败了。。。但这情况你重试也没用啊
    ksedz
        9
    ksedz  
    OP
       2023-06-20 11:27:00 +08:00
    @codehz 原来是这个意思,那直接成功就没什么问题了。
    ksedz
        10
    ksedz  
    OP
       2023-06-20 11:28:57 +08:00
    @neoblackcap 比较奇怪的是 poll 可以 epoll 不行,我查了设备驱动相关的知识也得到了实现 poll 能同时支持 select / poll / epoll 的结论,只是好像在设备驱动不实现 poll 时好像有比较奇怪的默认行为(内核版本 3.10 ),还需要细究和实验验证。
    codehz
        11
    codehz  
       2023-06-20 12:09:00 +08:00
    @ksedz poll 和 select 都是不支持 poll 的时候直接返回,原因多半是因为缺少错误报告的手段(用 fdset ,报错的时候你咋知道是哪个的问题),而 epoll 可以在 ctl 的时候返回报错,这就是原因
    ksedz
        12
    ksedz  
    OP
       2023-06-20 17:24:28 +08:00
    @codehz 佩服老哥内核功力,这就明白很多了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2587 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 05:29 · PVG 13:29 · LAX 21:29 · JFK 00:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.