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

337 天前
 ksedz
int fd = open("/dev/urandom", O_RDONLY);

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

求问什么原因


用 select 也正常

1448 次点击
所在节点    程序员
12 条回复
pagxir
337 天前
原因是这个设备驱动没有实现 epoll 的支持
ksedz
337 天前
@pagxir 完全知识盲区了,之前一直以为 epoll 是可以覆盖 select/poll 的功能的。谢谢解答,我补补知识点。
codehz
337 天前
但是 urandom 按定义就是不会阻塞的,你这个 epoll 也没意义啊。。。
urandom/random 只实现了 read_iter, write_iter, unlocked_ioctl, compat_ioctl, fasync, llseek, splice_read, splice_write 这几个方法,没有实现 poll 方法,因此 epoll 不能用(
至于 poll 和 select 系统调用,当发现目标没有实现 poll 方法的时候,直接就原样放回去了(假装都可读写)
ksedz
337 天前
@codehz 我是跟 openssl 1.0.2 的时候跟踪到的,在 RAND_poll 中调用了对相应设备的 poll 。项目要求所有的 select / poll 都要转为 epoll 集中处理,就遇到了这个问题。
查资料 /dev/urandom 在极端情况下会失败,那虽然它不阻塞,还是要去 poll/epoll 的。但如果设备驱动没有实现对应 poll 方法这就很尴尬了,只能直接在 hook 里让它调用成功了。谢谢讲解。
codehz
337 天前
@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
337 天前
epoll 不是什么类型的 fd 都支持的,我记得文件文件 FD 就不支持而 kqueue(FreeBSD)则是支持。
为了解决这个问题,所以又诞生了一个 inotify 。
当然了 timerfd ,epoll 是支持的。
ksedz
336 天前
@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
336 天前
@ksedz 那是用户态提供 buffer 有问题的情况才会失败,这个角度所有设计 buffer 的 syscall 你都得考虑失败了。。。但这情况你重试也没用啊
ksedz
336 天前
@codehz 原来是这个意思,那直接成功就没什么问题了。
ksedz
336 天前
@neoblackcap 比较奇怪的是 poll 可以 epoll 不行,我查了设备驱动相关的知识也得到了实现 poll 能同时支持 select / poll / epoll 的结论,只是好像在设备驱动不实现 poll 时好像有比较奇怪的默认行为(内核版本 3.10 ),还需要细究和实验验证。
codehz
336 天前
@ksedz poll 和 select 都是不支持 poll 的时候直接返回,原因多半是因为缺少错误报告的手段(用 fdset ,报错的时候你咋知道是哪个的问题),而 epoll 可以在 ctl 的时候返回报错,这就是原因
ksedz
336 天前
@codehz 佩服老哥内核功力,这就明白很多了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/950074

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX