2013-02-27 13 views
8

bloccaggio ho implementato un file meccanismo lungo le linee della suggestione dalla pagina linux man per "aprire" il blocco, in cui si afferma: programmiImplementazione di un file portatile meccanismo

portatili che vogliono eseguire il blocco dei file atomica utilizzando un file di blocco e si deve evitare di fare affidamento sul supporto NFS per O_EXCL, può creare un file univoco sullo stesso file system (ad esempio, incorporando nomehost e PID) e utilizzare il collegamento (2) per creare un collegamento al file di blocco . Se il collegamento (2) restituisce 0, il blocco ha esito positivo. In caso contrario, utilizzare stat (2) su il file univoco per verificare se il conteggio dei collegamenti è aumentato a 2, in in questo caso anche il blocco ha esito positivo.

Questo sembra funzionare perfettamente, tuttavia per ottenere una copertura 100% del codice nel mio test, ho bisogno di coprire il caso in cui la conta dei collegamenti è aumentato a 2.

Ho provato googling, ma tutti Mi sembra di essere in grado di trovare lo stesso riferimento sopra rigurgitato come "il modo in cui è fatto".

Qualcuno può spiegarmi quale insieme di circostanze causerebbe il fallimento del collegamento (restituisce -1), ma il conteggio dei collegamenti è aumentato a 2?

+0

Molto buona domanda . Non riesco a pensare a nessuna circostanza in cui ciò accada, a meno che due processi in conflitto abbiano entrambi scelto lo stesso nome di file unico allo stesso tempo (il che ovviamente sarebbe negativo). Potrebbe essere una soluzione alternativa per vecchi bug NFS? – Celada

+1

Hai bisogno di creare lockfile su NFS?AFAIK dovresti essere in grado di usare 'flock()' o 'lockf()' nella maggior parte dei casi. – Hasturkun

risposta

2

La risposta alla tua domanda è prevista in fondo il link (2) la pagina del manuale di Linux Programmer:

On NFS file systems, the return code may be wrong in case the NFS 
    server performs the link creation and dies before it can say so. Use 
    stat(2) to find out if the link got created. 
1

La creazione di un altro file è più un problema che altro. Creare invece una directory e controllare il risultato della creazione. Il manuale Unix afferma che solo una attività può riuscire a creare una directory, l'altra otterrà un errore se la directory esiste già, incluso il caso in cui 2 task lo ha provato nello stesso momento. Il sistema operativo stesso gestisce il problema in modo da non doverlo fare.

Se non fosse per eventuali blocchi bloccati, è tutto quello che dovresti fare. Tuttavia, le cose accadono, i programmi si interrompono e non rimuovono sempre il blocco. Quindi l'implementazione può essere un po 'più elaborata.

In uno script ho spesso utilizzato il codice seguente. Gestisce automaticamente le serrature obsolete. È possibile implementare la stessa in C. pagina Cerca man:

man -s 2 mkdir 

EXECUTION_CONTROL_FILE: è un percorso nome e il nome Dir, qualcosa come/usr/tmp/myAppName

second_of_now: restituire l'ora corrente in secondi (riportato qui di seguito)

LOCK_MAX_TIME: è quanto tempo in secondi una lattina di blocco esiste prima di essere considerato stantio

sonno 5: e 'sempre presume che un blocco farà qualcosa di breve e dolce. In caso contrario, forse il tuo ciclo del sonno dovrebbe essere più lungo.

LockFile() { 
    L_DIR=${EXECUTION_CONTROL_FILE}.lock 
    L_DIR2=${EXECUTION_CONTROL_FILE}.lock2 
    (
    L_STATUS=1 
    L_FILE_COUNT=2 
    L_COUNT=10 
    while [ $L_STATUS != 0 ]; do 
    mkdir $L_DIR 2>/dev/null 
    L_STATUS=$? 
    if [ $L_STATUS = 0 ]; then 
     # Create the timetime stamp file 
     second_of_now >$L_DIR/timestamp 
    else 
     # The directory exists, check how long it has been there 
     L_NOW=`second_of_now` 
     L_THEN=`cat $L_DIR/timestamp 2>/dev/null` 
     # The file does not exist, how many times did this happen? 
     if [ "$L_THEN" = "" ]; then 
     if [ $L_FILE_COUNT != 0 ]; then 
      L_THEN=$L_NOW 
      L_FILE_COUNT=`expr $L_FILE_COUNT - 1` 
     else 
      L_THEN=0 
     fi 
     fi 
     if [ `expr $L_NOW - $L_THEN` -gt $LOCK_MAX_TIME ]; then 
     # We will try 10 times to unlock, but the 10th time 
     # we will force the unlock. 
     UnlockFile $L_COUNT 
     L_COUNT=`expr $L_COUNT - 1` 
     else 
     L_COUNT=10 # Reset this back in case it has gone down 
     sleep 5 
     fi 
    fi 
    done 
) 
    L_STATUS=$? 
    return $L_STATUS 
} 

#### 
#### Remove access lock 
#### 
UnlockFile() { 
    U_DIR=${EXECUTION_CONTROL_FILE}.lock 
    U_DIR2=${EXECUTION_CONTROL_FILE}.lock2 
    (
    # This 'cd' fixes an issue with UNIX which sometimes report this error: 
    # rm: cannot determine if this is an ancestor of the current working directory 
    cd `dirname "${EXECUTION_CONTROL_FILE}"` 

    mkdir $U_DIR2 2>/dev/null 
    U_STATUS=$? 
    if [ $U_STATUS != 0 ]; then 
    if [ "$1" != "0" ]; then 
     return 
    fi 
    fi 

    trap "rm -rf $U_DIR2" 0 

    # The directory exists, check how long it has been there 
    # in case it has just been added again 
    U_NOW=`second_of_now` 
    U_THEN=`cat $U_DIR/timestamp 2>/dev/null` 
    # The file does not exist then we assume it is obsolete 
    if [ "$U_THEN" = "" ]; then 
    U_THEN=0 
    fi 
    if [ `expr $U_NOW - $U_THEN` -gt $LOCK_MAX_TIME -o "$1" = "mine" ]; then 
    # Remove lock directory as it is still too old 
    rm -rf $U_DIR 
    fi 

    # Remove this short lock directory 
    rm -rf $U_DIR2 
) 
    U_STATUS=$? 
    return $U_STATUS 
} 

#### 
second_of_now() { 
    second_of_day `date "+%y%m%d%H%M%S"` 
} 

#### 
#### Return which second of the date/time this is. The parameters must 
#### be in the form "yymmddHHMMSS", no centuries for the year and 
#### years before 2000 are not supported. 
second_of_day() { 
    year=`printf "$1\n"|cut -c1-2` 
    year=`expr $year + 0` 
    month=`printf "$1\n"|cut -c3-4` 
    day=`printf "$1\n"|cut -c5-6` 
    day=`expr $day - 1` 
    hour=`printf "$1\n"|cut -c7-8` 
    min=`printf "$1\n"|cut -c9-10` 
    sec=`printf "$1\n"|cut -c11-12` 
    sec=`expr $min \* 60 + $sec` 
    sec=`expr $hour \* 3600 + $sec` 
    sec=`expr $day \* 86400 + $sec` 
    if [ `expr 20$year % 4` = 0 ]; then 
    bisex=29 
    else 
    bisex=28 
    fi 
    mm=1 
    while [ $mm -lt $month ]; do 
    case $mm in 
     4|6|9|11) days=30 ;; 
     2) days=$bisex ;; 
     *) days=31 ;; 
    esac 
    sec=`expr $days \* 86400 + $sec` 
    mm=`expr $mm + 1` 
    done 
    year=`expr $year + 2000` 
    while [ $year -gt 2000 ]; do 
    year=`expr $year - 1` 
    if [ `expr $year % 4` = 0 ]; then 
     sec=`expr 31622400 + $sec` 
    else 
     sec=`expr 31536000 + $sec` 
    fi 
    done 
    printf "$sec\n" 
} 

Usa come questo:

# Make sure that 2 operations don't happen at the same time 
    LockFile 
    # Make sure we get rid of our lock if we exit unexpectedly 
    trap "UnlockFile mine" 0 
. 
. Do what you have to do 
. 
    # We need to remove the lock 
    UnlockFile mine 
+0

Che sembra filante, IMHO. Qualunque sia l'atomicità che hai ottenuto dal tuo 'mkdir()' (che, btw, puoi anche ottenere dalla creazione di un file con 'O_EXCL') perdi più tardi quando accedi al file timestamp. Se sei in uno script di shell, probabilmente stai meglio usando il comando 'flock' di util-linux. – Hasturkun

+0

L'intento qui era di descrivere una tecnica che utilizza esclusivamente il linguaggio di script e potrebbe comunque essere compatibile con il codice C che vorrebbe accedere alla stessa risorsa. L'utilizzo di "mkdir" e la verifica del codice di ritorno sarebbero stati sufficienti, ma ciò non avrebbe consentito il blocco stantio. Se il meccanismo di blocco è utilizzato esclusivamente dal codice C, allora avete a disposizione funzioni molto migliori e sono d'accordo, questo sarebbe più di un dolore. Mi chiedo perché, con tutti gli strumenti disponibili in Unix, non sia stato scritto nessuno strumento (o che sia stato preso in considerazione) per manipolare i blocchi negli script. – cpu

+0

Uno strumento esiste per manipolare i blocchi negli script, come ho detto prima, l'utilità [flock] (http://man7.org/linux/man-pages/man1/flock.1.html) da [util-linux ] (https://www.kernel.org/pub/linux/utils/util-linux/) pacchetto lo fa. vedi anche http://stackoverflow.com/a/1985512 – Hasturkun