int fd = open("/dev/urandom", O_RDONLY);
加入 epoll 会报错 EPERM
用 poll 可以正常得到结果
求问什么原因
用 select 也正常
1
pagxir 2023-06-19 18:15:38 +08:00 via Android 1
原因是这个设备驱动没有实现 epoll 的支持
|
2
ksedz OP @pagxir 完全知识盲区了,之前一直以为 epoll 是可以覆盖 select/poll 的功能的。谢谢解答,我补补知识点。
|
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 方法的时候,直接就原样放回去了(假装都可读写) |
4
ksedz OP @codehz 我是跟 openssl 1.0.2 的时候跟踪到的,在 RAND_poll 中调用了对相应设备的 poll 。项目要求所有的 select / poll 都要转为 epoll 集中处理,就遇到了这个问题。
查资料 /dev/urandom 在极端情况下会失败,那虽然它不阻塞,还是要去 poll/epoll 的。但如果设备驱动没有实现对应 poll 方法这就很尴尬了,只能直接在 hook 里让它调用成功了。谢谢讲解。 |
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 ),就直接返回可读即可 |
6
neoblackcap 2023-06-19 21:05:15 +08:00
epoll 不是什么类型的 fd 都支持的,我记得文件文件 FD 就不支持而 kqueue(FreeBSD)则是支持。
为了解决这个问题,所以又诞生了一个 inotify 。 当然了 timerfd ,epoll 是支持的。 |
7
ksedz OP @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 |
8
codehz 2023-06-20 10:57:42 +08:00
@ksedz 那是用户态提供 buffer 有问题的情况才会失败,这个角度所有设计 buffer 的 syscall 你都得考虑失败了。。。但这情况你重试也没用啊
|
10
ksedz OP @neoblackcap 比较奇怪的是 poll 可以 epoll 不行,我查了设备驱动相关的知识也得到了实现 poll 能同时支持 select / poll / epoll 的结论,只是好像在设备驱动不实现 poll 时好像有比较奇怪的默认行为(内核版本 3.10 ),还需要细究和实验验证。
|