
Вопрос
Я хотел бы иметь возможность запускать команду UNIXименно таккаждую секундув течение длительного периода времени.
Мне нужно решение, которое не отстает по истечении определенного времени, поскольку самой команде требуется время на выполнение.спать,смотреть, и определенныйскрипт на Pythonвсе меня подвели в этом отношении.
На микроконтроллерах, таких какhttp://Arduino.ccЯ бы сделал это через аппаратные прерывания часов. Я хотел бы узнать, есть ли похожее решение для скрипта оболочки с точным временем. Все решения, которые я нашел на StackExchange.com, привели к заметной задержке, если они выполнялись в течение нескольких часов. Подробности см. ниже.
Практическое назначение/применение
Я хочу проверить, постоянно ли работает мое сетевое соединение, отправляя временные метки через nc
(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
Найдено по адресу:Повторять команду Unix каждые x секунд вечно
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
POSIX-стандартualarm()
Функция позволяет вам запланировать ядру периодическую отправку сигналов вашему процессу с точностью до микросекунды.
Составьте простую программу:
#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 минут без единого сбоя, и я подозреваю, что так будет продолжаться, если только а) нагрузка на систему не станет настолько высокой, что fork() будет занимать больше секунды, или б) не будет добавлена дополнительная секунда.
Однако он не может гарантировать, что команда будет выполняться с точным секундным интервалом, поскольку возникают некоторые накладные расходы, но я сомневаюсь, что это намного хуже, чем решение на основе прерываний.
Я запустил его примерно на час с date +%N
(наносекундами, расширением GNU) и запустил некоторую статистику. Наибольшая задержка составила 1 155 микросекунд. Среднее (среднее арифметическое) 216 мкс, медиана 219 мкс, стандартное отклонение 42 мкс. Он работал быстрее 270 мкс в 95% случаев. Я не думаю, что вы сможете превзойти его, кроме как программой на C.