在将这个答案写在这里给另一个问答时,我注意到 zsh 解决方案中用于休眠给定厘秒数的时间select()
比我预期的要差很多。
这似乎是由于select()
我的系统超时似乎有一个一致的漂移,就好像它的秒数值是正常秒数的 1.001 一样。
$ strace -qqTe /select zsh -c 'zmodload zsh/zselect; zselect -t 6000'
pselect6(0, 0x7ffcee6a9f60, 0x7ffcee6a9fe0, 0x7ffcee6aa060, {tv_sec=60, tv_nsec=0}, NULL) = 0 (Timeout) <60.058930>
$ strace -qqTe /select zsh -c 'zmodload zsh/zselect; zselect -t 600'
pselect6(0, 0x7ffcf2042b70, 0x7ffcf2042bf0, 0x7ffcf2042c70, {tv_sec=6, tv_nsec=0}, NULL) = 0 (Timeout) <6.006212>
$ uname -rs
Linux 6.7.12-amd64
看看 6 秒超时如何花费 6.006 秒,以及 60 秒超时如何花费 60.06 秒。
我在不同的硬件上观察到了同样的情况,Ubuntu 22.04 和 Linux 6.5.0。
相同,perl
而不是zsh
:
$ strace -qqTe /select perl -e 'select undef,undef,undef,60'
pselect6(0, NULL, NULL, NULL, {tv_sec=60, tv_nsec=0}, NULL) = 0 (Timeout) <60.060218>
同样,使用 poll() 代替 select():
$ strace -qqTe /poll perl -MIO::Poll -e 'IO::Poll->new()->poll(6)'
poll([], 0, 6000) = 0 (Timeout) <6.006215>
$ strace -qqTe /poll perl -MIO::Poll -e 'IO::Poll->new()->poll(60)'
poll([], 0, 60000) = 0 (Timeout) <60.060214>
这似乎不适用于clock_nanosleep()
或alarm()
。
这是一个已知问题吗?甚至可能是故意的吗?
这是有意为之,尽管没有作为内核接口的一部分记录下来。
select
超时有一些余地,目前¹常规任务的超时为 0.1%,上限为 100 毫秒(“nice” 任务为 0.5%):在安排超时时使用余地;它定义了内核出于功率或性能原因将唤醒移回的余地有多大。
select
由于时间是在设置计时器之前和唤醒任务之后花费的,因此这个余量并不是您在上看到的最大延迟;但它应该接近上限:如果某项任务的计时器松弛量 (
/proc/pid/timerslack_ns
) 高于计算出的select
松弛量,则改用:减少懈怠的唯一方法就是使任务实时化:
¹自 2008 年 2.6.28 起…