问题

问题

问题

我希望能够运行 UNIX 命令恰恰每一秒在很长一段时间内

我需要一个解决方案,该解决方案不会由于命令本身执行所需的时间而在一定时间后落后。睡觉,手表,以及一定的蟒蛇脚本在这方面我都失败了。

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

实际目的/应用

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

发件人:

precise-timestamp-generator | tee netcat-sender.txt | nc $receiver $port

接收者:

nc -l -p $port > netcat-receiver.txt

完成后,对比两条日志:

diff netcat-sender.txt netcat-receiver.txt

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


解决方案 睡眠

while [ true ]; do date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done | tee timelog-sleep.txt

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

精确

cat timelog-sleep.txt

2012-07-16 00:45:16
[...]
2012-07-16 10:20:36

经过秒数:34520

wc -l timelog-sleep.txt

文件行数: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

本来是为了避免时间偏移,但未能做到。

精确

wc -l timelog-repeat-py.txt

2012-07-16 13:42:44
[...]
2012-07-16 16:45:24

经过秒数:10960

wc -l timelog-repeat-py.txt

文件行数:10859

精度总结:

  • 10960-10859 = 101 个时序问题
  • 10960/10859 = 1.009 = 0.9% 折扣

解决方案 观看

watch -n 1 "date '+%Y-%m-%d %H:%M:%S' >> ~/Desktop/timelog-watch.txt"

精确

wc -l timelog-watch.txt
2012-07-16 11:04:08
[...]
2012-07-16 13:25:47

经过秒数:8499

wc -l timelog-watch.txt

文件行数:8366

精度总结:

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

答案1

您尝试过watch使用参数--precise吗?

watch -n 1 --precise "date '+%Y-%m-%d %H:%M:%S.%N' >> ~/Desktop/timelog-watch.txt"

从手册页:

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

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

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

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

2561 lines
start: 2012-07-17 09:46:34.938805108
end:   2012-07-17 10:29:14.938547796

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

答案2

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();
 }

编译

 gcc -O2 tick.c -o tick

然后将其附加到您需要定期执行的任何操作,如下所示:

./tick | while read x; do
    date "+%Y-%m-%d %H:%M:%S"
done | tee timelog-sleep.txt

答案3

crontab分辨率为1分钟。如果您同意每分钟累积滞后时间然后在下一分钟重置,那么这个基本想法可能会起作用:

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

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

然而,根据产生的延迟程度sleep,第 59 秒有可能与下一分钟的第 0 秒重叠。

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

$ cat timelog-cron
2012-07-16 20:51:01
...
2012-07-16 22:43:00

1 小时 52 分钟 = 6720 秒

$ wc -l timelog-cron
6720 timelog-cron

0 计时问题,0% 折扣。任何时间累积每分钟都会重置。

答案4

我刚刚编写的这个 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();
}

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

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

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

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

相关内容