Intereting Posts
Как остановить grub от возиться с моим разделом EFI? Копирование папок с устройства MTP с использованием libmtp (через USB) Grep, которые содержат «1111-11-11» или «2382-21-20». (Любые номера) USB Ubuntu с шифрованием всего диска Как сосуществуют расширенные (4MB) и регулярные (4KB) поисковые системы? Как перенести данные с сервера на сервер с минимальным временем простоя? Как управлять error_log и access_log apache? Невозможно обновить микрокод Intel с помощью apt-get Почему nautilus в фоновом режиме занимает столько IO и CPU при входе в систему, и как это разрешить, или это ошибка? Как каталог является «особым типом файла»? Почему AWK не любит точки в одной из двух подстановок? ssh password-less login не работает с помощью proxycommand С точки зрения безопасности я должен добавить своего пользователя в файл sudoers или нет? Манипулировать имя файла с помощью команды find Как проверить, содержит ли каталог те же файлы в архиве TAR?

Запускайте команду unix точно через очень короткие промежутки времени, не задерживая время задержки во времени

Вопрос

Я хотел бы иметь возможность запускать команду UNIX точно каждую секунду в течение длительного периода времени .

Мне нужно решение, которое не отстает через определенное время из-за времени, которое команда сама должна выполнить. сон , часы и некий сценарий на питоне в этом отношении не помогли.

На микроконтроллере, таком как http://Arduino.cc, я бы сделал это через аппаратные прерывания. Я хотел бы знать, есть ли аналогичное время-точное решение сценария оболочки. Все решения, которые я нашел в StackExchange.com, привели к заметному временному отставанию, если они работают в течение нескольких часов. См. Подробности ниже.

Практическое назначение / применение

Я хочу протестировать мое сетевое соединение непрерывно, отправив timestamps через nc (netcat) каждые 1 секунду.

Отправитель:

 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 создает проблемы.


Решение SLEEP

 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%

Решение REPEAT 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

 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%.

Как этот скрипт 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 () занимает больше секунды или b) вставлен прыжок.

Однако он не может гарантировать, что команда выполняется с точными вторыми интервалами, так как есть некоторые накладные расходы, но я сомневаюсь, что это намного хуже, чем решение на основе прерываний.

Я провел его около часа с date +%N (наносекунды, расширение GNU) и запустил некоторые статистические данные об этом. Наибольшее отставание составляло 1 155 микросекунд. Среднее (среднее арифметическое) 216 мкс, медиана 219 мкс, стандартное отклонение 42 мкс. Он работал быстрее, чем 270 мкс в 95% случаев. Я не думаю, что вы можете победить его, кроме как с помощью программы C.

Функция 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 

Вы пробовали watch с параметром --precise ?

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

На странице руководства:

Обычно этот интервал интерпретируется как промежуток времени между завершением одного запуска команды и началом следующего прогона. Однако с параметром -p или -precise вы можете сделать попытку запуска команды запускать каждый интервал секунд. Попробуйте с 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

crontab имеет разрешение 1 минуту. Если вы в порядке с накоплением времени запаздывания в течение этой минуты, а затем сбрасыванием на следующую минуту, эта основная идея может работать:

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

Обратите внимание, что script.sh также запускается в фоновом режиме. Это должно помочь свести к минимуму отставание, которое накапливается с каждой итерацией цикла.

В зависимости от того, сколько генерирует sleep , есть вероятность того, что второй 59 перекрывается со вторым 0 следующей минуты.

EDIT, чтобы подбросить некоторые результаты в том же формате, что и в вопросе:

 $ 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% выкл. Любое время накопления сбрасывается каждую минуту.

Ваша проблема в том, что вы спите в течение определенного количества времени после запуска своей программы, не принимая во внимание количество времени, прошедшее с момента последнего спада.

Вы можете сделать это bash или любой другой язык программирования, но ключ должен использовать часы, чтобы определить, как долго планировать следующий сон. Перед сном проверьте часы, посмотрите, сколько времени у вас осталось, и сонте разницу.

Из-за компромиссов по планированию процесса вам не гарантируется просыпаться прямо на такте, но вы должны быть достаточно близки (в течение нескольких мс выгружены или в течение нескольких сотен мс под нагрузкой). И вы не будете накапливать ошибку с течением времени, потому что каждый раз, когда вы повторно синхронизируете каждый цикл сна и удаляете любую накопленную ошибку.

Если вам нужно точно указать часы, то то, что вы ищете, – это операционная система реального времени , которая предназначена именно для этой цели.

Я всегда просто отказывался от того, что что-то работало с интервалом. Я думаю, вам придется написать программу на C и очень внимательно следить за тем, чтобы не превышать часть 1-секундного интервала с вашим собственным кодом. Вероятно, вам придется использовать потоки или несколько процессов взаимодействия, чтобы заставить это работать. Следите за тем, чтобы избежать начального или начального времени процесса.

Одна из ссылок, которая, по-видимому, относится к 1993 году: рандомизированные часы выборки для оценки использования ЦП и профилирования кода. Вы хотите взглянуть на приложение «Код исходного кода», чтобы узнать, как они точно измеряют интервалы времени и «проснутся», их программа в правильное время. Поскольку код составляет 19 лет, он, вероятно, не будет переноситься напрямую или легко, но если вы его прочтете и попытаетесь понять, используемые принципы могут привести ваш код.

EDIT: нашел еще одну ссылку, которая могла бы помочь: Эффекты разрешения часов при планировании интерактивных и мягких процессов реального времени. Это должно помочь вам с любым теоретическим фоном.

Взгляните на nanosleep () (из http://linux.about.com/library/cmd/blcmdl2_nanosleep.htm ). Вместо того чтобы сделать вашу программу спящей 1 секунду, сделайте ее спящей (1 – количество, необходимое для запуска) секунд. Вы получите гораздо лучшее разрешение.

Попробуйте запустить вашу команду в фоновом режиме, чтобы она не влияла на время цикла так много, но даже этого будет недостаточно, если вы не хотите накопления в течение длительных периодов времени, поскольку, конечно, с ней связано несколько миллисекундных затрат.

Таким образом, это, вероятно, лучше, но также, вероятно, все еще недостаточно хорошо:

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

На моем компьютере это дало 2 ошибки за 20 минут или 0,1 в минуту, что примерно на пять раз выше по сравнению с вашим пробегом.

Уродливый, но он работает. Вероятно, вы должны переосмыслить дизайн своей программы, если вам нужен такой цикл. Он в основном проверяет, соответствует ли текущая целая секунда предыдущей проверке и печатает количество наносекунд с момента изменения второго. На точность влияет сон .001.

 while true; do T=$( date +%s ); while [[ $T -eq $( date +%s ) ]]; do sleep .001; done; date "+%N nanoseconds late"; done 

Точность находится в миллисекундах, при условии, что date "+%N nanoseconds late" полезной нагрузки» date "+%N nanoseconds late" не занимает больше времени, чем чуть меньше секунды. Вы можете снизить нагрузку на процессор, увеличив период ожидания или если вы действительно не возражаете, просто замените команду sleep на true .

 002112890 nanoseconds late 001847692 nanoseconds late 002273652 nanoseconds late 001317015 nanoseconds late 001650504 nanoseconds late 002180949 nanoseconds late 002338716 nanoseconds late 002064578 nanoseconds late 002160883 nanoseconds late 

Это плохая практика, потому что вы в основном делаете опрос центрального процессора для события, и вы тратите впустую циклы процессора. Вероятно, вы хотите подключиться к прерыванию по таймеру (это невозможно из bash) или использовать специализированное оборудование, такое как микроконтроллер. ПК и его операционная система не предназначены для высокой точности синхронизации.

Другим методом будет использование приостановления в цикле и отправка SIGCONT из точной внешней программы. Отправка сигнала очень легкая и будет иметь гораздо меньшую задержку, чем выполнение чего-либо. Вы также можете предварительно поставить очередь команд с командой «at», вряд ли кто-нибудь использует «на» больше, я не уверен, насколько это точно.

Если точность важна, и вы хотите серьезно относиться к этому, это звучит как приложение, в котором обычно используется RTOS, что может быть сделано в Linux с RT-Preempt исправленным ядром, что даст вам точность и некоторую степень контроль прерывания, но это может быть больше беспокоить, чем это стоит.

https://rt.wiki.kernel.org/index.php/RT_PREEMPT_HOWTO

Xenomai также может быть полезен, это полная реализация RTOS и переносится на x86 и x86_64, но есть некоторые программы.

http://www.xenomai.org/index.php/Main_Page

С ksh93 (с плавающей точкой $SECONDS и встроенным sleep )

 typeset -F SECONDS=0 typeset -ii=0 while true; do cmd sleep "$((++i - SECONDS))" done 

Тот же скрипт будет работать и с zsh но вызовет команду sleep вашей системы. zsh имеет встроенный zselect , но только с разрешением 1/100.

Я бы пошел с небольшой программой на C:

 #include <sys/time.h> #include <unistd.h> int main(int argc, char **argv, char **envp) { struct timeval start; int rc = gettimeofday(&start, NULL); if(rc != 0) return 1; for(;;) { struct timeval now; rc = gettimeofday(&now, NULL); useconds_t delay; if(now.tv_usec < start.tv_usec) delay = start.tv_usec - now.tv_usec; else delay = 1000000 - now.tv_usec + start.tv_usec; usleep(delay); pid_t pid = fork(); if(pid == -1) return 1; if(pid == 0) _exit(execve(argv[1], &argv[1], envp)); } } 

Эта программа ожидает, что программа вызовет полный путь в качестве своего первого аргумента и передаст все остальные аргументы. Он не будет ждать завершения команды, поэтому он с радостью начнет несколько экземпляров.

Кроме того, стиль кодирования здесь неаккуратный, и делается ряд предположений, которые могут или не могут быть гарантированы применимыми стандартами, то есть качество этого кода «работает для меня».

Эта программа получит несколько более длинные или короткие интервалы, когда часы будут настроены NTP или вручную настроены. Если программа должна справиться с этим, POSIX предоставляет timer_create(CLOCK_MONOTONIC, ...) который это не влияет.

Вы должны следить за текущим временем и сравнивать его с временем начала. Таким образом, вы спите расчетное количество времени на каждую итерацию, а не фиксированную сумму. Таким образом, вы не будете накапливать ошибки времени и отходить от того места, где вы должны быть, потому что вы сбрасываете свои тайминги каждый цикл в абсолютное время с самого начала.

Также некоторые функции сна возвращаются раньше, если есть interupt, поэтому в этом случае вам придется снова вызвать ваш метод сна, пока не пройдет полный промежуток времени.

Вот один из них – сценарий bash и очень точный. Он использует usleep для микросекундной точности.

http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron

Там есть три скрипта. Посмотрите на нижнюю. С точностью до 2400 выстрелов в минуту с разумной точностью. И это очень просто.

Это может работать не менее 100 раз в секунду с очень точным разрешением.

Наличие каталога числа циклов в минуту создает расписание. Эта версия поддерживает разрешение в микросекундах, предполагая, что ваш компьютер может справиться с этим. Количество казней в минуту не обязательно равномерно делится на 60 и не ограничивается 60, я тестировал его до 6000, и он работает.

Эта версия может быть установлена ​​в каталоге /etc/init.d и запущена как служба.

 #! /bin/sh # chkconfig: 2345 91 61 # description: This program is used to run all programs in a directory in parallel every X times per minute. \ # Think of this program as cron with microseconds resolution. # Microsecond Cron # Usage: cron-ms start # Copyright 2014 by Marc Perkel # docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron" # Free to use with attribution # The scheduling is done by creating directories with the number of" # executions per minute as part of the directory name." # Examples: # /etc/cron-ms/7 # Executes everything in that directory 7 times a minute # /etc/cron-ms/30 # Executes everything in that directory 30 times a minute # /etc/cron-ms/600 # Executes everything in that directory 10 times a second # /etc/cron-ms/2400 # Executes everything in that directory 40 times a second basedir=/etc/cron-ms case "$1" in start|restart|reload) $0 stop mkdir -p /var/run/cron-ms for dir in $basedir/* ; do $0 ${dir##*/} & done exit ;; stop) rm -Rf /var/run/cron-ms exit ;; esac # Loops per minute is passed on the command line loops=$1 interval=$((60000000/$loops)) # Just a heartbeat signal that can be used with monit to verify it's alive touch /var/run/cron-ms # After a restart the PIDs will be different allowing old processes to terminate touch /var/run/cron-ms/$$ # Sleeps until a specific part of a minute with microsecond resolution. 60000000 is full minute usleep $(( $interval - 10#$(date +%S%N) / 1000 % $interval )) # Deleting the PID files exit the program if [ ! -f /var/run/cron-ms/$$ ] then exit fi # Run all the programs in the directory in parallel for program in $basedir/$loops/* ; do if [ -x $program ] then $program &> /dev/null & fi done exec $0 $loops и #! /bin/sh # chkconfig: 2345 91 61 # description: This program is used to run all programs in a directory in parallel every X times per minute. \ # Think of this program as cron with microseconds resolution. # Microsecond Cron # Usage: cron-ms start # Copyright 2014 by Marc Perkel # docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron" # Free to use with attribution # The scheduling is done by creating directories with the number of" # executions per minute as part of the directory name." # Examples: # /etc/cron-ms/7 # Executes everything in that directory 7 times a minute # /etc/cron-ms/30 # Executes everything in that directory 30 times a minute # /etc/cron-ms/600 # Executes everything in that directory 10 times a second # /etc/cron-ms/2400 # Executes everything in that directory 40 times a second basedir=/etc/cron-ms case "$1" in start|restart|reload) $0 stop mkdir -p /var/run/cron-ms for dir in $basedir/* ; do $0 ${dir##*/} & done exit ;; stop) rm -Rf /var/run/cron-ms exit ;; esac # Loops per minute is passed on the command line loops=$1 interval=$((60000000/$loops)) # Just a heartbeat signal that can be used with monit to verify it's alive touch /var/run/cron-ms # After a restart the PIDs will be different allowing old processes to terminate touch /var/run/cron-ms/$$ # Sleeps until a specific part of a minute with microsecond resolution. 60000000 is full minute usleep $(( $interval - 10#$(date +%S%N) / 1000 % $interval )) # Deleting the PID files exit the program if [ ! -f /var/run/cron-ms/$$ ] then exit fi # Run all the programs in the directory in parallel for program in $basedir/$loops/* ; do if [ -x $program ] then $program &> /dev/null & fi done exec $0 $loops