以非常短的时间间隔精确运行 unix 命令,而不会随着时间的推移累积时间延迟

por*_*org 38 cron real-time

我希望能够在很长一段时间内精确地每秒运行一个 UNIX 命令。

我需要一个解决方案,它在一段时间后不会落后,因为命令本身需要执行时间。sleepwatch和某个python 脚本在这方面都让我失望。

在诸如http://Arduino.cc之类的微控制器上,我会通过硬件时钟中断来做到这一点。我想知道是否有类似的时间精确的shell脚本解决方案。我在 StackExchange.com 中找到的所有解决方案,如果运行超过几个小时,都会导致明显的时间延迟。请参阅下面的详细信息。

实际用途/应用

我想通过nc每 1 秒通过(netcat)发送时间戳来测试我的网络连接是否持续连接。

发件人:

precise-timestamp-generator | tee netcat-sender.txt | nc $receiver $port
Run Code Online (Sandbox Code Playgroud)

接收者:

nc -l -p $port > netcat-receiver.txt
Run Code Online (Sandbox Code Playgroud)

完成后,比较两个日志:

diff netcat-sender.txt netcat-receiver.txt
Run Code Online (Sandbox Code Playgroud)

差异将是未传输的时间戳。由此我可以知道我的 LAN / WAN / ISP 什么时候出问题。


解决方案 睡眠

while [ true ]; do date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done | tee timelog-sleep.txt
Run Code Online (Sandbox Code Playgroud)

随着时间的推移获得一定的偏移量,因为循环中的命令也需要一点时间。

精确

cat timelog-sleep.txt

2012-07-16 00:45:16
[...]
2012-07-16 10:20:36
Run Code Online (Sandbox Code Playgroud)

经过的秒数:34520

wc -l timelog-sleep.txt
Run Code Online (Sandbox Code Playgroud)

文件中的行:34243

精度总结:

  • 34520-34243 = 277 计时问题
  • 34520/34243 = 1.008 = 0.8%

解决方案重复 PYTHON

发现于:永远每 x 秒重复一次 Unix 命令

repeat.py 1 "date '+%Y-%m-%d %H:%M:%S'" >> timelog-repeat-py.txt
Run Code Online (Sandbox Code Playgroud)

应该避免时间偏移,但没有这样做。

精确

wc -l timelog-repeat-py.txt

2012-07-16 13:42:44
[...]
2012-07-16 16:45:24
Run Code Online (Sandbox Code Playgroud)

经过的秒数:10960

wc -l timelog-repeat-py.txt
Run Code Online (Sandbox Code Playgroud)

文件中的行数:10859

精度总结:

  • 10960-10859 = 101 计时问题
  • 10960/10859 = 1.009 = 0.9%

解决方案观察

watch -n 1 "date '+%Y-%m-%d %H:%M:%S' >> ~/Desktop/timelog-watch.txt"
Run Code Online (Sandbox Code Playgroud)

精确

wc -l timelog-watch.txt
2012-07-16 11:04:08
[...]
2012-07-16 13:25:47
Run Code Online (Sandbox Code Playgroud)

经过的秒数:8499

wc -l timelog-watch.txt
Run Code Online (Sandbox Code Playgroud)

文件中的行数:8366

精度总结:

  • 8499-8366 = 133 个计时问题。
  • 8499/8366 = 1.016 = 1.6% 折扣。

Dav*_*ave 29

POSIXualarm()函数允许您安排内核以微秒精度定期向您的进程发送信号。

编写一个简单的程序:

 #include<unistd.h>
 #include<signal.h>
 void tick(int sig){
     write(1, "\n", 1);
 }
 int main(){
     signal(SIGALRM, tick);
     ualarm(1000000, 1000000); //alarm in a second, and every second after that.
     for(;;)
         pause();
 }
Run Code Online (Sandbox Code Playgroud)

编译

 gcc -O2 tick.c -o tick
Run Code Online (Sandbox Code Playgroud)

然后将它附加到您需要定期完成的任何事情上,如下所示:

./tick | while read x; do
    date "+%Y-%m-%d %H:%M:%S"
done | tee timelog-sleep.txt
Run Code Online (Sandbox Code Playgroud)


dan*_*ann 29

你试过watch参数--precise吗?

watch -n 1 --precise "date '+%Y-%m-%d %H:%M:%S.%N' >> ~/Desktop/timelog-watch.txt"
Run Code Online (Sandbox Code Playgroud)

从手册页:

通常,此间隔被解释为完成一次命令运行和下一次运行开始之间的时间量。但是,使用 -p 或 --precise 选项,您可以让 watch 尝试每隔 interval 秒运行一次命令。尝试使用 ntptime 并注意小数秒如何保持(几乎)相同,而不是它们持续增加的正常模式。

但是,该参数可能在您的系统上不可用。

您还应该考虑当您的程序执行需要超过一秒钟时会发生什么。应该跳过下一个预定的执行,还是应该延迟运行?

更新:我运行了一段时间脚本,它没有丢失任何一步:

2561 lines
start: 2012-07-17 09:46:34.938805108
end:   2012-07-17 10:29:14.938547796
Run Code Online (Sandbox Code Playgroud)

更新:--precise标志是 Debian 添加的,但补丁相当简单:http : //patch-tracker.debian.org/patch/series/view/procps/1 : 3.2.8-9squeeze1/watch_precision_time.patch


Izk*_*ata 19

crontab分辨率为 1 分钟。如果您对每分钟累积的延迟时间感到满意,然后在下一分钟重置,那么这个基本想法可能会奏效:

* * * * * for second in $(seq 0 59); do /path/to/script.sh & sleep 1s;done
Run Code Online (Sandbox Code Playgroud)

请注意,script.sh它也在后台运行。这应该有助于最小化循环每次迭代累积的滞后。

sleep然而,根据产生的滞后量,第 59 秒有可能与下一分钟的第 0 秒重叠。

编辑以与问题中相同的格式抛出一些结果:

$ cat timelog-cron
2012-07-16 20:51:01
...
2012-07-16 22:43:00
Run Code Online (Sandbox Code Playgroud)

1 小时 52 分 = 6720 秒

$ wc -l timelog-cron
6720 timelog-cron
Run Code Online (Sandbox Code Playgroud)

0 时间问题,0% 折扣。任何时间累积每分钟重置一次。

  • @hhaamu 它有什么难看的?PC 上的通用操作系统不是为非常精确的时间关键操作而设计的,那么您还能期待什么呢?如果您想要“优雅”且绝对精确的计时,则必须使用不同的 CPU 调度程序,或切换到实时内核,或使用专用硬件等。这是一个完全合法的解决方案,我认为没有任何理由投反对票。这当然是对只有“在后台运行”而没有通过 cron 定期重新同步的人的改进。 (3认同)
  • 这是一个丑陋的黑客 (2认同)

tyl*_*erl 15

您的问题是您在运行程序后睡眠了固定的时间,而没有考虑自上次睡眠以来已经过去的时间。

您可以使用 bash 或任何其他编程语言来执行此操作,但关键是使用时钟来确定安排下一次睡眠的时间。睡觉前,看看时钟,看看你还剩多少时间,然后睡一觉。

由于进程调度妥协,您不能保证在时钟滴答时立即醒来,但您应该相当接近(在卸载的几毫秒内,或负载下的几百毫秒内)。而且您不会随着时间的推移累积错误,因为每次您都会在每个睡眠周期重新同步并消除任何累积的错误。

如果您需要准确地按时计时,那么您正在寻找的是实时操作系统,它正是为此目的而设计的。


hha*_*amu 12

我刚刚编写的这个 Perl 脚本是如何工作的?

#!/usr/bin/perl

use strict;
use warnings;
use Time::HiRes qw/time sleep/;

sub launch {
    return if fork;
    exec @_;
    die "Couldn't exec";
}

$SIG{CHLD} = 'IGNORE';

my $interval = shift;
my $start = time();
while (1) {
    launch(@ARGV);
    $start += $interval;
    sleep $start - time();
}
Run Code Online (Sandbox Code Playgroud)

用: perl timer.pl 1 date '+%Y-%m-%d %H:%M:%S'

它已经运行了 45 分钟而没有一次跳过,我怀疑它会继续这样做,除非 a) 系统负载变得如此之高以至于 fork() 需要超过一秒或 b) 插入闰秒。

然而,它不能保证命令以精确的秒间隔运行,因为有一些开销,但我怀疑它比基于中断的解决方案差得多。

我用date +%N(纳秒,GNU 扩展)运行了大约一个小时,并对其进行了一些统计。它的最大延迟是 1155 微秒。平均值(算术平均值)216 µs,中位数 219 µs,标准偏差 42 µs。它在 95% 的时间内运行速度超过 270 µs。我认为除非通过 C 程序,您无法击败它。


Bru*_*ger 7

我总是放弃让某些东西完全按间隔运行。我认为您必须编写一个 C 程序,并非常小心地注意不要使用自己的代码超过 1 秒间隔的部分。您可能必须使用线程或多个相互通信的进程才能使其工作。注意避免线程启动或进程启动时间开销。

一个似乎与 1993 年相关的参考资料:用于 CPU 利用率估计和代码分析的随机采样时钟 您需要查看附录“对手源代码”,了解他们如何准确测量时间间隔,并“唤醒”他们的节目正好在正确的时间。由于代码已有 19 年的历史,因此可能无法直接或轻松移植,但如果您阅读并尝试理解它,所涉及的原则可能会指导您的代码。

编辑:找到另一个可能有帮助的参考资料:时钟分辨率对交互式和软实时进程的调度的影响, 这应该可以帮助您了解任何理论背景。