Czasami musisz się upewnić, że jednocześnie działa tylko jedna instancja skryptu powłoki.
Na przykład zadanie cron, które jest wykonywane przez crond, które nie zapewnia samodzielnego blokowania (np. Domyślny crond Solaris).
Typowym wzorem do implementacji blokowania jest następujący kod:
#!/bin/sh
LOCK=/var/tmp/mylock
if [ -f $LOCK ]; then # 'test' -> race begin
echo Job is already running\!
exit 6
fi
touch $LOCK # 'set' -> race end
# do some work
rm $LOCK
Oczywiście taki kod ma warunek wyścigu. Istnieje okno czasowe, w którym wykonanie dwóch wystąpień może przejść do następnego wiersza 3, zanim będzie można dotknąć $LOCK
pliku.
W przypadku zadania cron zwykle nie stanowi to problemu, ponieważ odstęp między kolejnymi wywołaniami wynosi kilka minut.
Ale coś może pójść nie tak - na przykład, gdy plik blokujący znajduje się na serwerze NFS - który się zawiesza. W takim przypadku kilka zadań cron może zablokować na linii 3 i ustawić w kolejce. Jeśli serwer NFS jest ponownie aktywny, oznacza to, że istnieje stado grzmotu równolegle uruchomionych zadań.
Podczas wyszukiwania w Internecie znalazłem narzędzie lockrun, które wydaje się dobrym rozwiązaniem tego problemu. Za jego pomocą uruchamiasz skrypt, który wymaga zablokowania w następujący sposób:
$ lockrun --lockfile=/var/tmp/mylock myscript.sh
Możesz to umieścić w opakowaniu lub użyć ze swojego crontab.
Wykorzystuje lockf()
(POSIX), jeśli jest dostępny, i wraca do flock()
(BSD). I lockf()
wsparcie przez NFS powinien być stosunkowo powszechne.
Czy są alternatywy dla lockrun
?
Co z innymi demonami cron? Czy istnieją popularne crondy, które obsługują blokowanie w rozsądny sposób? Szybkie spojrzenie na stronę manuala Vixie Crond (domyślnie w systemach Debian / Ubuntu) nie pokazuje nic na temat blokowania.
Czy dobrym pomysłem byłoby włączenie takiego narzędzia lockrun
do coreutils ?
Moim zdaniem realizuje motyw bardzo podobny do timeout
, nice
i przyjaciół.
kill
edycji; i wydaje się, że dobrą praktyką jest przechowywanie własnego pid w pliku blokującym, niż tylko dotykanie go.Odpowiedzi:
Oto inny sposób na zablokowanie w skrypcie powłoki, który może zapobiec opisanemu powyżej wyścigowi, w którym dwa zadania mogą przekroczyć linię 3.
noclobber
Opcja będzie działać w ksh i bash. Nie używaj,set noclobber
ponieważ nie powinieneś pisać skryptów w csh / tcsh. ;)YMMV z blokowaniem na NFS (wiesz, kiedy serwery NFS są nieosiągalne), ale ogólnie jest o wiele bardziej solidny niż kiedyś. (10 lat temu)
Jeśli masz zadania cron, które robią to samo w tym samym czasie, z wielu serwerów, ale potrzebujesz tylko 1 instancji, aby faktycznie uruchomić, coś takiego może działać dla Ciebie.
Nie mam doświadczenia z lockrun, ale posiadanie wstępnie ustawionego środowiska blokady przed uruchomieniem skryptu może pomóc. A może nie. Po prostu ustawiasz test pliku blokady poza skryptem w opakowaniu i teoretycznie, czy nie mógłbyś po prostu osiągnąć tego samego stanu wyścigu, gdyby lockrun wykonał dwa zadania dokładnie w tym samym czasie, tak jak w przypadku „inside- rozwiązanie skryptu?
Blokowanie plików i tak jest w zasadzie zachowaniem systemu honorowego, a wszelkie skrypty, które nie sprawdzają istnienia pliku blokującego przed uruchomieniem, zrobią wszystko, co zamierzają. Wystarczy, że przejdziesz test pliku blokady i odpowiednie zachowanie, rozwiążesz 99% potencjalnych problemów, jeśli nie 100%.
Jeśli często spotykasz się z warunkami wyścigu plików blokujących, może to wskazywać na większy problem, na przykład brak prawidłowego zaplanowania zadań, a może jeśli interwał nie jest tak ważny jak ukończenie zadania, być może Twoje zadanie lepiej nadaje się do demonizacji .
EDYTUJ PONIŻEJ - 2016-05-06 (jeśli używasz KSH88)
Oprzyj się na komentarzu @Clint Pachl poniżej, jeśli używasz ksh88, użyj
mkdir
zamiastnoclobber
. W większości przypadków łagodzi to potencjalny stan wyścigu, ale nie ogranicza go całkowicie (choć ryzyko jest niewielkie). Aby uzyskać więcej informacji, przeczytaj link zamieszczony poniżej przez Clinta .Dodatkową zaletą jest to, że jeśli musisz utworzyć pliki tmp w swoim skrypcie, możesz użyć
lockdir
dla nich katalogu, wiedząc, że zostaną one wyczyszczone po zakończeniu skryptu.Aby uzyskać bardziej nowoczesny bash, odpowiednia powinna być metoda noclobber na górze.
źródło
lockf()
wywołaniu systemowym - gdy zostanie ono wykonane, wszystkie procesy zostaną wznowione, ale tylko jeden proces wygra blokadę. Brak warunków wyścigu. Nie mam takich problemów z cronjobs - wręcz przeciwnie - ale jest to problem, gdy cię uderza, może powodować wiele bólu.set -o noclobber && echo "$$" > "$lockfile"
aby uzyskać bezpieczny powrót, gdy powłoka nie obsługuje opcji noclobber.noclobber
Opcja może być podatna na wyścig warunki. Zajrzyj na mywiki.wooledge.org/BashFAQ/045, aby znaleźć coś do przemyślenia.noclobber
(lub-C
) w ksh88 nie działa, ponieważ ksh88 nie używaO_EXCL
dlanoclobber
. Jeśli korzystasz z nowszej powłoki, możesz być w porządku ...Wolę używać twardych linków.
Twarde linki są atomowe w stosunku do NFS i w przeważającej części również mkdir . Używanie
mkdir(2)
lublink(2)
jest prawie takie samo, na poziomie praktycznym; Po prostu wolę używać twardych dowiązań, ponieważ więcej implementacji NFS dopuszcza atomowe dowiązania twarde niż atomowemkdir
. Dzięki nowoczesnym wersjom NFS nie powinieneś się martwić korzystaniem z jednego z nich.źródło
Rozumiem, że
mkdir
to atom, więc być może:źródło
mkdir()
ponad NFS (> = 3) jest standaryzowany jako atomowy.mkdir
do bycia atomowym (tak jestrename
). W praktyce wiadomo, że niektóre implementacje nie są. Powiązane: ciekawy wątek, w tym wkład autora GNU arch .Prostym sposobem jest użycie dostarczanego
lockfile
zwykle wraz zprocmail
pakietem.źródło
sem
który jest częściąparallel
narzędzi GNU , może być tym, czego szukasz:Jak w:
wyjście:
Pamiętaj, że zamówienie nie jest gwarantowane. Również wyjście nie jest wyświetlane, dopóki się nie skończy (irytujące!). Ale mimo to jest to najbardziej zwięzły sposób, jaki znam, aby uchronić się przed równoczesnym wykonywaniem, nie martwiąc się o pliki blokujące, próby i czyszczenie.
źródło
sem
klamkę zostało zestrzelone w trakcie wykonywania?Używam
dtach
.źródło
Korzystam z narzędzia wiersza polecenia „flock” do zarządzania blokadami w skryptach bash, jak opisano tutaj i tutaj . Użyłem tej prostej metody ze strony stada, aby uruchomić niektóre polecenia w podpowłoce ...
W tym przykładzie nie działa z kodem wyjścia 1, jeśli nie może uzyskać pliku blokującego. Ale flocka można również używać w sposób, który nie wymaga uruchamiania poleceń w podpowłoce :-)
źródło
flock()
Wywołanie systemowe nie działa przez NFS .flock()
i dlatego jest problematyczny w stosunku do NFS. W międzyczasie flock () w Linuksie teraz powraca dofcntl()
momentu, gdy plik znajduje się na wierzchowcu NFS, a zatem w środowisku NFS opartym tylko na Linuksieflock()
działa teraz na NFS.Nie używaj pliku.
Jeśli skrypt jest wykonywany w ten sposób, np .:
Możesz sprawdzić, czy działa, używając:
źródło
my_script
? W przypadku, gdy działa inne wystąpienie - nierunning_proc
zawiera dwóch pasujących wierszy? Podoba mi się pomysł, ale oczywiście - dostaniesz fałszywe wyniki, gdy inny użytkownik jest uruchomiony skrypt o tej samej nazwie ...$!
zamiast$$
w swoim przykładzie.grep -v $$
.). Zasadniczo próbowałem przedstawić inne podejście do problemu.Do faktycznego użycia należy użyć odpowiedzi, która została najwyżej oceniona .
Chciałbym jednak omówić różne zepsute i na wpół wykonalne podejścia
ps
oraz wiele ostrzeżeń, jakie mają, ponieważ wciąż widzę, jak ludzie z nich korzystają.Ta odpowiedź to tak naprawdę odpowiedź na pytanie „Dlaczego nie użyć
ps
igrep
poradzić sobie z blokowaniem w powłoce?”Zepsute podejście # 1
Po pierwsze, podejście podane w innej odpowiedzi, które ma kilka pozytywnych opinii, mimo że nie działa (i nigdy nie mogło) działać i najwyraźniej nigdy nie zostało przetestowane:
Naprawmy błędy składniowe i zepsute
ps
argumenty i uzyskajmy:Ten skrypt zawsze kończy działanie 6, za każdym razem, bez względu na to, jak go uruchomisz.
Jeśli uruchomisz go
./myscript
,ps
wynik będzie po prostu taki12345 -bash
, który nie będzie pasował do wymaganego ciągu12345 bash ./myscript
, więc to się nie powiedzie.Jeśli go uruchomisz
bash myscript
, wszystko stanie się bardziej interesujące. Proces bash widły do uruchomienia rurociągu, a dziecko powłoka uruchamiaps
igrep
. Zarówno powłoka oryginalna, jak i podrzędna zostaną wyświetlone w danychps
wyjściowych, mniej więcej tak:Nie jest to oczekiwany wynik
$$ bash $0
, więc skrypt zostanie zamknięty.Zepsute podejście # 2
Teraz, uczciwie wobec użytkownika, który napisał złamane podejście nr 1, zrobiłem coś podobnego, kiedy po raz pierwszy spróbowałem tego:
To prawie działa. Ale fakt rozwidlenia się, aby poprowadzić rurę, odrzuca to. Więc ten również zawsze wyjdzie.
Nierzetelne podejście # 3
Ta wersja pozwala uniknąć problemu rozwidlenia potoku w podejściu nr 2, najpierw pobierając wszystkie PID, które mają bieżący skrypt w argumentach wiersza poleceń, a następnie osobno filtrując tę listę pid, aby pominąć PID bieżącego skryptu.
Może to działać ... pod warunkiem, że żaden inny proces nie ma wiersza polecenia pasującego do
$0
, a jeśli skrypt jest zawsze wywoływany w ten sam sposób (np. Jeśli jest wywoływany ze ścieżką względną, a następnie ścieżką bezwzględną, druga instancja nie zauważy pierwszej ).Nierzetelne podejście # 4
A co jeśli pominiemy sprawdzanie pełnego wiersza poleceń, ponieważ może to nie oznaczać, że skrypt faktycznie działa, i
lsof
zamiast tego sprawdzimy, aby znaleźć wszystkie procesy, które mają ten skrypt otwarty?Cóż, tak, to podejście nie jest wcale takie złe:
Oczywiście, jeśli kopia skryptu jest uruchomiona, nowa instancja uruchomi się dobrze i będziesz mieć uruchomione dwie kopie .
Lub jeśli uruchomiony skrypt zostanie zmodyfikowany (np. Za pomocą Vima lub a
git checkout
), wówczas „nowa” wersja skryptu uruchomi się bez problemu, ponieważ zarówno Vim, jak igit checkout
nowy plik (nowy i-węzeł) zamiast stary.Jednakże, jeśli skrypt nie jest modyfikowana i nigdy kopiowane, to ta wersja jest bardzo dobra. Nie ma warunków wyścigu, ponieważ plik skryptu musi już być otwarty, aby można było sprawdzić czek.
Nadal mogą występować fałszywe alarmy, jeśli inny proces ma otwarty plik skryptu, ale pamiętaj, że nawet jeśli jest on otwarty do edycji w Vimie, vim tak naprawdę nie utrzymuje otwartego pliku skryptu, więc nie spowoduje to fałszywych alarmów.
Ale pamiętaj, nie używaj tego podejścia, jeśli skrypt może być edytowany lub kopiowany, ponieważ otrzymasz fałszywe negatywy, tj. Wiele instancji działających jednocześnie - więc fakt, że edycja za pomocą Vima nie daje fałszywych wyników pozytywnych, nie powinien mieć znaczenia do Ciebie. Wspominam go jednak, ponieważ podejście nr 3 nie daje wyników fałszywie dodatnich (tj odmawia start) jeśli masz skrypt otwarty z Vima.
Co więc zrobić?
Najwyższej głosowało odpowiedź na to pytanie daje solidne podejście.
Być może możesz napisać lepszy ... ale jeśli nie rozumiesz wszystkich problemów i zastrzeżeń we wszystkich powyższych podejściach, prawdopodobnie nie napiszesz metody blokowania, która omija je wszystkie.
źródło
Za pomocą narzędzia FLOM (Free LOck Manager) serializowanie poleceń staje się tak proste, jak uruchamianie
FLOM pozwala na implementację bardziej skomplikowanych przypadków użycia (blokowanie rozproszone, czytniki / zapisy, zasoby numeryczne itp.), Jak wyjaśniono tutaj: http://sourceforge.net/p/flom/wiki/FLOM%20by%20examples/
źródło
Oto coś, co czasami dodaję na serwerze, aby łatwo obsługiwać warunki wyścigu dla każdego zadania na maszynie. To jest podobne do postu Tima Kennedy'ego, ale w ten sposób dostajesz obsługę wyścigu, dodając tylko jeden wiersz do każdego skryptu bash, który tego potrzebuje.
Umieść poniższe treści w np. / Opt / racechecker / racechecker:
Oto jak z niego korzystać. Zwróć uwagę na wiersz po shebang:
Działa w ten sposób, że rozpoznaje nazwę głównego pliku bashscript i tworzy plik pid pod „/ tmp”. Dodaje także detektor do sygnału zakończenia. Słuchacz usunie plik pid, gdy główny skrypt poprawnie zakończy pracę.
Zamiast tego, jeśli plik pid istnieje podczas uruchamiania instancji, zostanie wykonana instrukcja if zawierająca kod w drugiej instrukcji if. W takim przypadku postanowiłem uruchomić alarmową pocztę, kiedy to nastąpi.
Co jeśli skrypt się zawiesi
Kolejnym ćwiczeniem byłoby radzenie sobie z awariami. Idealnie plik pidfile powinien zostać usunięty, nawet jeśli główny skrypt zawiedzie z jakiegokolwiek powodu, nie dzieje się tak w mojej wersji powyżej. Oznacza to, że w przypadku awarii skryptu plik pid musiałby zostać ręcznie usunięty, aby przywrócić funkcjonalność.
W przypadku awarii systemu
Dobrym pomysłem jest przechowywanie pliku pidfile / lockfile na przykład / tmp. W ten sposób skrypty będą nadal działać po awarii systemu, ponieważ pliki pid zawsze będą usuwane podczas uruchamiania.
źródło
Sprawdź mój skrypt ...
Możesz to UWIELBIAĆ ....
źródło
Oferuję następujące rozwiązanie w skrypcie o nazwie „flocktest”
źródło