GNU/Linux >> Znalost Linux >  >> Linux

Správné zamykání skriptů Shell?

Někdy se musíte ujistit, že současně běží pouze jedna instance skriptu shellu.

Například úloha cron, která se provádí přes crond, který sám o sobě neposkytuje
zamykání (např. výchozí crond Solaris).

Běžným vzorem pro implementaci zamykání je kód jako tento:

#!/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

Takový kód má samozřejmě rasovou podmínku. Je zde časové okno, ve kterém
provedení dvou instancí může postoupit po řádku 3, než se jeden může
dotknout $LOCK soubor.

U úlohy cron to obvykle není problém, protože mezi dvěma vyvolání je interval
minut.

Ale věci se mohou pokazit – například když je lockfile na serveru NFS –
který se zasekne. V takovém případě může několik úloh cron zablokovat na řádku 3 a zařadit se do fronty. Pokud je
server NFS opět aktivní, pak máte hromové stádo paralelně
běžících úloh.

Při hledání na webu jsem našel nástroj lockrun, který se zdá být dobrým
řešením tohoto problému. Pomocí něj spustíte skript, který potřebuje zamykání, jako je
toto:

$ lockrun --lockfile=/var/tmp/mylock myscript.sh

Můžete to dát do obalu nebo to použít z crontab.

Používá lockf() (POSIX), pokud je k dispozici, a vrátí se zpět na flock() (BSD). A lockf() podpora přes NFS by měla být poměrně rozšířená.

Existují alternativy k lockrun ?

A co ostatní cron démoni? Existují běžné crondy, které podporují zamykání
rozumným způsobem? Rychlý pohled do manuálové stránky Vixie Crond (výchozí na
systémech Debian/Ubuntu) neukazuje nic o zamykání.

Bylo by dobré zahrnout nástroj jako lockrun do coreutils?

Podle mého názoru implementuje téma velmi podobné timeout , nice a přátelé.

Přijatá odpověď:

Zde je další způsob, jak provést uzamčení ve skriptu shellu, který může zabránit sporu, který popisujete výše, kde dvě úlohy mohou projít řádkem 3. noclobber volba bude fungovat v ksh a bash. Nepoužívejte set noclobber protože byste neměli skriptovat v csh/tcsh. 😉

lockfile=/var/tmp/mylock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then

        trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT

        # do stuff here

        # clean up after yourself, and release your trap
        rm -f "$lockfile"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockfile owned by $(cat $lockfile)"
fi

YMMV se zamykáním na NFS (znáte, když NFS servery nejsou dostupné), ale obecně je mnohem robustnější, než býval. (před 10 lety)

Pokud máte úlohy cronu, které dělají totéž ve stejnou dobu, z více serverů, ale ke skutečnému spuštění potřebujete pouze 1 instanci, může vám něco takového fungovat.

Související:jednoduchý příkaz MacOS Bluetooth Toggle Shell?

S lockrunem nemám žádné zkušenosti, ale mít přednastavené zamykací prostředí před skutečným spuštěním skriptu by mohlo pomoci. Nebo nemusí. Právě nastavujete test pro lockfile mimo váš skript v obalu a teoreticky byste nemohli jednoduše dosáhnout stejného sporu, pokud by lockrun zavolal dvě úlohy přesně ve stejnou dobu, stejně jako v případě 'inside- řešení skriptu?

Zamykání souborů je tak jako tak do značné míry čestným chováním systému a všechny skripty, které před spuštěním nezkontrolují existenci souboru zámku, udělají vše, co udělají. Pouhým provedením testu lockfile a správným chováním vyřešíte 99 % potenciálních problémů, ne-li 100 %.

Pokud často narážíte na závody lockfile, může to být indikátorem většího problému, jako je nesprávné načasování úloh nebo možná, pokud interval není tak důležitý jako dokončení úlohy, možná je vaše úloha vhodnější pro démonizaci. .

UPRAVIT NÍŽE – 2016-05-06 (pokud používáte KSH88)

Na základě komentáře @Clint Pachla níže, pokud používáte ksh88, použijte mkdir místo noclobber . To většinou zmírňuje potenciální rasovou podmínku, ale neomezuje ji úplně (ačkoli riziko je minimální). Pro více informací si přečtěte odkaz, který Clint zveřejnil níže.

lockdir=/var/tmp/mylock
pidfile=/var/tmp/mylock/pid

if ( mkdir ${lockdir} ) 2> /dev/null; then
        echo $$ > $pidfile
        trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT
        # do stuff here

        # clean up after yourself, and release your trap
        rm -rf "$lockdir"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockdir owned by $(cat $pidfile)"
fi

A jako další výhodu, pokud potřebujete vytvořit tmpfiles ve svém skriptu, můžete použít lockdir adresář pro ně s vědomím, že budou po ukončení skriptu vyčištěny.

Pro modernější bash by měla být vhodná metoda noclobber na vrcholu.


Linux
  1. Povolit skripty Setuid On Shell?

  2. Asociativní pole ve skriptech Shell?

  3. Jak zrušit kořenová oprávnění ve skriptech Shell?

  1. Skrýváte heslo ve skriptech Shell?

  2. Jak spouštět skripty Pythonu ze shellu

  3. Jak zopakovat nový řádek ve skriptech Bash Shell

  1. Spouštět skripty Shell přes web?

  2. Rozšíření souborů pro skripty Unix Shell?

  3. Sdílení proměnných ve více skriptech Shell?