Pytanie
Chciałbym móc uruchomić polecenie UNIX dokładnie co sekundę przez długi czas .
Potrzebuję rozwiązania, które nie pozostaje w tyle po pewnym czasie, ponieważ czas potrzebny na wykonanie samego polecenia. spanie , oglądanie i pewien skrypt Pythona zawiodły mnie pod tym względem.
Na mikrokontrolerze, takim jak http://Arduino.cc , zrobiłbym to przez przerwania zegara sprzętowego. Chciałbym wiedzieć, czy istnieje podobne precyzyjne rozwiązanie skryptów powłoki. Wszystkie rozwiązania, które znalazłem w StackExchange.com, spowodowały zauważalne opóźnienie, jeśli działały przez wiele godzin. Szczegóły poniżej.
Praktyczny cel / zastosowanie
Chcę sprawdzić, czy moje połączenie sieciowe jest stale nc
aktywne, wysyłając znaczniki czasu za pośrednictwem (netcat) co 1 sekundę.
Nadawca:
precise-timestamp-generator | tee netcat-sender.txt | nc $receiver $port
Odbiorca:
nc -l -p $port > netcat-receiver.txt
Po zakończeniu porównaj dwa dzienniki:
diff netcat-sender.txt netcat-receiver.txt
Różnice byłyby nieprzesłanymi znacznikami czasu. Z tego wiedziałbym, o której godzinie mój LAN / WAN / ISP sprawia problemy.
Rozwiązanie SLEEP
while [ true ]; do date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done | tee timelog-sleep.txt
Pobiera pewne przesunięcie w czasie, ponieważ polecenie w pętli również zajmuje trochę czasu.
Precyzja
cat timelog-sleep.txt
2012-07-16 00:45:16
[...]
2012-07-16 10:20:36
Upłynęły sekundy: 34520
wc -l timelog-sleep.txt
Linie w pliku: 34243
Precyzja podsumowana:
- 34520-34243 = 277 problemów z synchronizacją
- 34520/34243 = 1,008 = 0,8% zniżki
Rozwiązanie POWTÓRZ PYTHON
Znalezione w: Powtarzaj polecenie Unix co x sekund na zawsze
repeat.py 1 "date '+%Y-%m-%d %H:%M:%S'" >> timelog-repeat-py.txt
Zakłada się, aby uniknąć przesunięcia czasowego, ale tego nie robi.
Precyzja
wc -l timelog-repeat-py.txt
2012-07-16 13:42:44
[...]
2012-07-16 16:45:24
Upłynęły sekundy: 10960
wc -l timelog-repeat-py.txt
Linie w pliku: 10859
Precyzja podsumowana:
- 10960-10859 = 101 problemów z synchronizacją
- 10960/10859 = 1,009 = 0,9% zniżki
ROZWIĄZANIE ROZWIĄZANIA
watch -n 1 "date '+%Y-%m-%d %H:%M:%S' >> ~/Desktop/timelog-watch.txt"
Precyzja
wc -l timelog-watch.txt
2012-07-16 11:04:08
[...]
2012-07-16 13:25:47
Upłynęły sekundy: 8499
wc -l timelog-watch.txt
Linie w pliku: 8366
Precyzja podsumowana:
- 8499-8366 = 133 problemy z synchronizacją.
- 8499/8366 = 1.016 = 1,6% zniżki.
nice
śpiący?Odpowiedzi:
Jak działa ten skrypt Perla, który właśnie ulepszyłem?
Posługiwać się:
perl timer.pl 1 date '+%Y-%m-%d %H:%M:%S'
Pracuje 45 minut bez jednego przeskoku i podejrzewam, że będzie to kontynuował, chyba że a) obciążenie systemu stanie się tak wysokie, że fork () zajmie więcej niż sekundę lub b) wstawiona zostanie sekunda przestępna.
Nie może jednak zagwarantować, że polecenie będzie uruchamiane dokładnie w drugich odstępach, ponieważ istnieje pewne obciążenie, ale wątpię, że jest znacznie gorsze niż rozwiązanie oparte na przerwie.
Uruchomiłem go przez około godzinę z
date +%N
(nanosekundami, rozszerzeniem GNU) i przeprowadziłem statystyki na ten temat. Największe opóźnienie miało 1 155 mikrosekund. Średnia (średnia arytmetyczna) 216 µs, mediana 219 µs, odchylenie standardowe 42 µs. Działał szybciej niż 270 µs w 95% przypadków. Nie sądzę, że można go pokonać inaczej niż programem C.źródło
GNU date
z+%N
, a po zaledwie 3 minutach wyrzucił ten błąd:Time::HiRes::sleep(-0.00615549): negative time not invented yet at ~/bin/repeat.pl line 23.
Linia 23 w moim zapisanego skryptu:sleep $start - time();
Funkcja POSIX
ualarm()
pozwala zaplanować jądro, aby okresowo sygnalizować proces z mikrosekundową precyzją.Przygotuj prosty program:
Skompilować
Następnie dołącz go do wszystkiego, co musisz robić okresowo:
źródło
-std=c99
nie otrzymasz ostrzeżenia o brakującym zwrocie. W przeciwnym razie nie potrzebujesz niczego specjalnego. Czy źle wpisałeś dodatkowe zero?strace ./tick
pokaże ci, co robi z perspektywy systemowejPróbowałeś już
watch
z parametrem--precise
?Ze strony podręcznika:
Ten parametr może jednak nie być dostępny w twoim systemie.
Powinieneś również rozważyć, co powinno się stać, gdy wykonanie programu wymaga więcej niż jednej sekundy. Czy należy pominąć kolejne zaplanowane wykonanie, czy też powinno się je spóźnić?
Aktualizacja : Uruchomiłem skrypt przez jakiś czas i nie stracił ani jednego kroku:
Aktualizacja:
--precise
flaga jest dodanie Debian, plaster jest jednak dość prosta: http://patch-tracker.debian.org/patch/series/view/procps/1:3.2.8-9squeeze1/watch_precision_time.patchźródło
watch
obsługuje tę opcję? Nie było go na żadnej z maszyn, które sprawdziłem.--precise
. To jest dodatek do Debiana (3.2.8-9, watch_precision_time.patch)2012-07-24 07:20:21.864818595 2012-07-24 07:20:22.467458430 2012-07-24 07:20:23.068575669 2012-07-24 07:20:23.968415439
Rzeczy w czasie rzeczywistym (jądro itp.) Jest tam z jakiegoś powodu!crontab
ma rozdzielczość 1 minuty. Jeśli nie masz nic przeciwko gromadzeniu się opóźnienia w tej minucie, a następnie resetowaniu w następnej minucie, ten podstawowy pomysł może zadziałać:Zauważ, że
script.sh
działa również w tle. Powinno to pomóc zminimalizować opóźnienie, które gromadzi się przy każdej iteracji pętli.W zależności od tego, ile
sleep
generuje opóźnienie , istnieje prawdopodobieństwo, że drugie 59 pokryje się z drugim 0 następnej minuty.EDYCJA, aby podrzucić niektóre wyniki, w tym samym formacie, co w pytaniu:
1 godzina 52 minuty = 6720 sekund
0 problemów z synchronizacją, 0% zniżki. Akumulacja czasu resetuje się co minutę.
źródło
cron
jest dokładny co do sekundy, ale to nie w tym przypadku w ogóle.Problem polega na tym, że śpisz przez określony czas po uruchomieniu programu, nie biorąc pod uwagę ilości czasu, który upłynął od ostatniego snu.
Możesz to zrobić w trybie bash lub innym języku programowania, ale kluczem jest użycie zegara, aby określić, jak długo zaplanować następny sen. Przed snem sprawdź zegar, sprawdź, ile czasu ci pozostało i spij różnicę.
Z powodu kompromisów w planowaniu procesów nie ma gwarancji, że obudzisz się natychmiast po taktowaniu zegara, ale powinieneś być dość blisko (w ciągu kilku ms rozładowane lub kilkaset ms pod obciążeniem). I nie gromadzisz błędów w czasie, ponieważ za każdym razem ponowna synchronizacja w każdym cyklu snu i usuwanie wszelkich nagromadzonych błędów.
Jeśli chcesz dokładnie zaznaczyć zegar, to szukasz systemu operacyjnego w czasie rzeczywistym , który został zaprojektowany właśnie do tego celu.
źródło
date +%S.%N
aby uzyskać liczbę sekund z precyzją poniżej sekundy iusleep
spać z precyzją poniżej sekundy, ale potem to tylko kwestia matematyki.Zawsze po prostu rezygnowałem z tego, że coś działa dokładnie w odstępach czasu. Myślę, że będziesz musiał napisać program w języku C i zwracać szczególną uwagę, aby nie przekroczyć części 1-sekundowego przedziału z własnym kodem. Prawdopodobnie będziesz musiał użyć wątków lub wielu wzajemnie komunikujących się procesów, aby to zadziałało. Uważaj, aby uniknąć nadmiernego czasu rozpoczynania wątku lub uruchamiania procesu.
Jedno odniesienie, które wydaje się mieć związek z 1993 r .: Randomizowany zegar próbkowania do szacowania wykorzystania procesora i profilowania kodu. Spójrz na dodatek „Kod źródłowy przeciwnika”, aby zobaczyć, jak dokładnie mierzyli odstępy czasu i „budzili się” ich program we właściwym czasie. Ponieważ kod ma 19 lat, prawdopodobnie nie można go przenieść bezpośrednio lub łatwo, ale jeśli go przeczytasz i spróbujesz go zrozumieć, zasady mogą poprowadzić twój kod.
EDYCJA: Znaleziono kolejne odniesienie, które może pomóc: Wpływ rozdzielczości zegara na planowanie interaktywnych i miękkich procesów w czasie rzeczywistym, który powinien pomóc ci na dowolnym tle teoretycznym.
źródło
Spójrz na nanosleep () (z http://linux.about.com/library/cmd/blcmdl2_nanosleep.htm ). Zamiast uśpienia programu na 1 sekundę, należy go uśpić (1 - kwota do uruchomienia) sekund. Otrzymasz znacznie lepszą rozdzielczość.
źródło
sleep
, tjsleep 0.99
. Problem polega na tym, że czas potrzebny do uruchomienia jest daleki od stałego, nawet jego średnia wartość może zmieniać się w czasie.Spróbuj uruchomić komendę w tle, aby nie miało to tak dużego wpływu na taktowanie pętli, ale nawet to nie wystarczy, jeśli nie chcesz żadnej akumulacji przez długi czas, ponieważ z pewnością wiąże się to z kosztem kilku milisekund.
Tak więc jest to prawdopodobnie lepsze, ale także prawdopodobnie wciąż niewystarczające:
Na moim komputerze dawało to 2 błędy w ciągu 20 minut lub 0,1 na minutę, co jest mniej więcej pięciokrotną poprawą w trakcie biegu.
źródło
sleep 1
polega na tym, że gwarantuje spanie co najmniej jedną sekundę - nigdy mniej. Stąd błąd się kumuluje.Brzydkie, ale działa. Prawdopodobnie powinieneś przemyśleć projekt swojego programu, jeśli potrzebujesz takiej pętli. Zasadniczo sprawdza, czy bieżąca cała sekunda jest równa poprzedniej sprawdzonej i drukuje liczbę nanosekund od zmiany drugiej. Na dokładność ma wpływ sen 0,001.
Dokładność jest w milisekundach, pod warunkiem, że „ładowność”
date "+%N nanoseconds late"
nie zajmie więcej niż sekundę. Możesz zmniejszyć obciążenie procesora, wydłużając okres uśpienia lub jeśli naprawdę nie masz nic przeciwko, po prostu zastąp polecenie uśpienia przeztrue
.Jest to zła praktyka, ponieważ po prostu sondujesz procesor w poszukiwaniu zdarzenia i marnujesz cykle procesora. Prawdopodobnie chcesz dołączyć do przerwania timera (niemożliwe z bash) lub użyć dedykowanego sprzętu, takiego jak mikrokontroler. Komputer PC i jego system operacyjny nie są zaprojektowane z myślą o wysokiej dokładności taktowania.
źródło
Inną metodą byłoby użycie zawieszenia w pętli i wysłanie SIGCONT z precyzyjnego programu zewnętrznego. Wysłanie sygnału jest bardzo lekkie i będzie miało znacznie mniejsze opóźnienie niż wykonanie czegoś. Możesz także ustawić w kolejce kilka poleceń za pomocą polecenia „at”, prawie nikt już nie używa „at”. Nie jestem pewien, jak dokładnie jest to możliwe.
Jeśli precyzja jest kluczowa i chcesz poważnie o tym podejść, brzmi to jak aplikacja, w której zwykle używasz RTOS, co można by zrobić pod Linuksem z łatanym jądrem RT-Preempt, co da ci precyzję i pewną miarę przerwać kontrolę, ale może to być bardziej kłopotliwe niż warte.
https://rt.wiki.kernel.org/index.php/RT_PREEMPT_HOWTO
Xenomai również może być pomocny, jest to pełna implementacja RTOS i jest portowana na x86 i x86_64, ale wiąże się to z pewnym programowaniem.
http://www.xenomai.org/index.php/Main_Page
źródło
Z
ksh93
(który ma zmiennoprzecinkowy$SECONDS
i wbudowanysleep
)Ten sam skrypt również będzie działał,
zsh
ale wywołasleep
polecenie systemu .zsh
mazselect
wbudowane, ale tylko w rozdzielczości 1/100.źródło
Wybrałbym mały program C:
Ten program oczekuje, że program zadzwoni z pełną ścieżką jako pierwszym argumentem i przekaże pozostałe argumenty. Nie będzie czekać na zakończenie polecenia, więc z przyjemnością uruchomi wiele instancji.
Także styl kodowania jest tutaj naprawdę niedbały i poczyniono szereg założeń, które mogą, ale nie muszą być zagwarantowane przez obowiązujące standardy, tj. Jakość tego kodu jest „dla mnie”.
Ten program będzie miał nieco dłuższe lub krótsze interwały, gdy zegar jest regulowany przez NTP lub ręcznie. Jeśli program powinien to obsłużyć, POSIX zapewnia to, na
timer_create(CLOCK_MONOTONIC, ...)
co nie ma to wpływu.źródło
Powinieneś śledzić aktualny czas i porównywać go z czasem rozpoczęcia. Więc śpisz obliczoną ilość czasu każdej iteracji, a nie stałą ilość. W ten sposób nie będziesz kumulować błędów pomiaru czasu i odejdziesz od miejsca, w którym powinieneś być, ponieważ od początku resetujesz czasy każdej pętli do czasu bezwzględnego.
Również niektóre funkcje snu wracają wcześniej, jeśli nastąpi przerwa, więc w takim przypadku będziesz musiał ponownie wywołać metodę snu, aż upłynie pełny czas.
źródło
Oto skrypt bashowy i bardzo dokładny. Używa usleep dla mikrosekundowej precyzji.
http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron
Tam są 3 skrypty. Spójrz na ten na dole. Wykonuje do około 2400 egzekucji na minutę z rozsądną dokładnością. I to jest naprawdę proste.
źródło
Ten może działać co najmniej 100 razy na sekundę z bardzo dokładną rozdzielczością.
Istnienie katalogu z liczbą pętli na minutę tworzy harmonogram. Ta wersja obsługuje rozdzielczość mikrosekundową, zakładając, że komputer może to obsłużyć. Liczba egzekucji na minutę nie musi być równomiernie podzielna przez 60, ani nie jest ograniczona do 60. Przetestowałem to do 6000 i działa.
Tę wersję można zainstalować w katalogu /etc/init.d i uruchomić jako usługę.
źródło