2015-10-13 11 views
11

Sul mio sistema (Ubuntu Linux, glibc), la pagina man di una chiamata close specifica diversi valori di ritorno di errore che può restituire. Dice ancheCosa fare se una chiamata di chiusura posix fallisce?

La mancata verifica del valore restituito di close() è un errore di programmazione comune ma comunque grave.

e allo stesso tempo

Si noti che il valore di ritorno deve essere utilizzato solo per la diagnostica. In particolare, close() non dovrebbe essere ripetuto dopo un EINTR poiché ciò potrebbe causare la chiusura di un descrittore riutilizzato da un altro thread.

Quindi non mi è consentito ignorare il valore restituito né riprovare la chiamata.

Dato che, come devo gestire l'errore di chiamata close()?

Se l'errore si è verificato durante la scrittura di qualcosa nel file, probabilmente dovrei provare a scrivere le informazioni da qualche altra parte per evitare la perdita di dati.

Se stavo solo leggendo il file, posso solo registrare l'errore e continuare il programma facendo finta che non sia successo niente? Ci sono avvertimenti, perdite di descrittori di file o quant'altro?

+0

pensato a questo. (http://unix.stackexchange.com/questions/231677/failing-close-system-call) I fallimenti ravvicinati hanno senso in alcuni casi (ad esempio, errori di sincronizzazione del disco), ma penso che dovrebbe essere sicuro assumere non fallire in alcuni altri casi. Come chiudere un'istanza di un filedescriptor duplicato che non è l'ultima istanza che punta allo stesso file fisico o che chiude una pipe, perché sarebbero fondamentalmente bug del kernel, ma mi piacerebbe ricevere una risposta più illuminata. – PSkocik

+1

FWIW, Raymond Chen interpreta questo tipo di situazione generale: http://blogs.msdn.com/b/oldnewthing/archive/2008/01/07/7011066.aspx –

+0

Qualunque cosa tu faccia, * fa sempre sapere all'utente * . Basta "loggarlo" in qualche file di log interno che nessuno guarda mai non è abbastanza; vorrai che l'utente sappia che sta succedendo qualcosa di strano. Per le applicazioni GUI, vorrei aprire una finestra di dialogo modale. Per le applicazioni della riga di comando, stamperei un avvertimento sull'errore standard. Per i servizi, il file di registro è sufficiente. Se l'errore 'close()' si verifica dopo aver scritto su un file, interrompo esattamente nello stesso modo in cui verrei se avessi riscontrato un errore di scrittura durante la scrittura sul file. –

risposta

6

In pratica, close dovrebbe mai da riprovare in caso di errore, e il fd passato a close è sempre valido (chiuso) dopo close rendimenti, indipendentemente dal fatto che si è verificato un errore. In alcuni casi, un errore potrebbe indicare che i dati sono stati persi (alcune configurazioni NFS) o condizioni hardware insolite per i dispositivi (ad esempio, il nastro non può essere riavvolto), quindi si consiglia di essere prudenti per evitare la perdita di dati, ma non si dovrebbe mai tentare di chiudi di nuovo il file.

In teoria, POSIX non era chiaro in passato se il fd rimane aperto quando non riesce con EINTR ei sistemi non sono d'accordo. Dal momento che è importante conoscere lo stato (altrimenti si hanno fd leaks o double-close bug estremamente pericolosi nei programmi multithread), la risoluzione a Austin Group issue #529 specificava il comportamento rigorosamente per le future versioni di POSIX, che EINTR significa che il fd rimane aperto.Questo è il comportamento corretto coerente con la definizione di EINTR altrove, ma Linux si rifiuta di accettarlo. (FWIW c'è una soluzione facile per questo che è possibile a livello di wrapper libc syscall, vedi glibc PR #14627.) Fortunatamente non si pone mai in pratica comunque.

Alcune questioni connesse si potrebbe trovare informativo:

+0

quindi se su 'EINTR' il file fd rimane aperto, ¿continua ad esserci un errore per provare a chiuderlo di nuovo? Cosa diavolo dovremmo fare in quel caso?La chiusura di un descrittore non valido dovrebbe restituire 'EINVAL', quindi non è un problema provare di nuovo (nonostante ciò che il manuale dice di altri thread apri i descrittori di file senza conoscenza, cosa dovrebbe accadere se altri thread aprono un descrittore nel caso in cui ci si trovi nel metà di un reindirizzamento IO? --- questa è una procedura a due fasi) Hmmm ... arent stiamo cercando di forzare troppo la macchina? –

+0

@LuisColorado: secondo lo standard (modificato), su 'EINTR' il file fd rimane aperto. Tuttavia, Linux non lo onora e glibc non aggira l'incapacità di onorarlo. Vedi i link nella mia risposta. Fortunatamente 'EINTR' non si verifica in' close' su Linux in situazioni del mondo reale di cui sono a conoscenza, comunque. –

+0

'EINTR' può succedere su' close (2) 'su filesystem NFS, connessioni di rete (beh, su socket TCP/IP di rete il kernel fa effettivamente il lavoro, ma non è sicuro su altri protocolli), così come su ogni dispositivo che ha bisogno che l'handshaking sia chiuso (nell'ultima cosa vicina, a seconda del driver del dispositivo da cui tornare da vicino) E linux non è il * solo * sistema POSIX esistente. –

1

Prima di tutto: EINTR significa esattamente che: la chiamata di sistema è stata interrotta, se questo accade su una chiamata close(), non c'è proprio nulla che tu possa fare.

Oltre a tenere traccia del fatto, che se il file appartiene a un file, questo file è probabilmente corrotto, non c'è molto che si possa fare sugli errori su close() a seconda del valore restituito. AFAIK l'unico caso, in cui una chiusura può essere ritentata è il EBUSY, ma devo ancora vederlo.

Quindi:

  • Non controllo il risultato della close() potrebbe significare che si dimentica la corruzione del file, in particolare il troncamento.
  • A seconda dell'errore, la maggior parte delle volte non si può fare nulla: un errore close() significa semplicemente che qualcosa è andato storto erroneamente al di fuori dell'ambito dell'applicazione.
+0

'EINTR' significa che syscall è stato interrotto e non verrà ritentato, quindi non è stato eseguito affatto. Il sistema non è stato eseguito, quindi il descrittore di file non è chiuso e deve essere chiuso. E l'atomicità delle chiamate di sistema? Se la chiamata di sistema non è stata eseguita e non possiamo chiudere nuovamente il descrittore, per quanto riguarda la ripetizione di questo processo un paio di volte con una perdita di un descrittore ciascuno? Normalmente, l'implementazione di chiamate che bloccano (come close(), ma non per i file normali) semplicemente annulla ciò che è stato fatto e rende un 'longjmp (3)' nel contesto salvato, solo per conservare l'atomicità. –

+0

@LuisColorado Riprovare 'close()' dopo 'EINTR' non è una buona idea su Linux. Potrebbe chiudere un fd diverso. –

+0

Gli autori dei driver di dispositivo sono sempre avvisati sulla chiusura primitiva, poiché deve eseguire il proprio lavoro in modo indipendente dalle condizioni dell'hardware, restituendo un ambiente stabile. Ciò significa che i writer dei driver di periferica devono talvolta lasciare le risorse bloccate in memoria per attendere che il dispositivo risponda o contrassegnarlo come inutilizzabile fino a qualche condizione, ma il processo si è disconnesso tempo fa. Pensa che i driver di periferica non siano normalmente scritti dagli stessi ragazzi che scrivono il sistema operativo. Non sto parlando specificamente di Linux, ma di POSIX, che significa molti sistemi diversi. –

Problemi correlati