Che cos'è un processo ininterrotto?


156

A volte ogni volta che scrivo un programma in Linux e si blocca a causa di un bug di qualche tipo, diventerà un processo ininterrotto e continuerà a funzionare per sempre fino al riavvio del computer (anche se esco). Le mie domande sono:

  • Che cosa rende un processo ininterrotto?
  • Come posso impedire che ciò accada?
  • Questa è probabilmente una domanda stupida, ma c'è un modo per interromperla senza riavviare il computer?

È possibile che un programma possa essere scritto per avviare un processo che passa in uno TASK_UNINTERUPTIBLEstato ogni volta che il sistema non è in uno stato inattivo, in tal modo raccogliendo forzatamente i dati, in attesa di trasmettere una volta uscito il superutente? Questa sarebbe una miniera d'oro per gli hacker per recuperare informazioni, tornare allo stato di zombi e trasmettere informazioni attraverso la rete in idle. Alcuni possono sostenere che questo è un modo per creare un Blackdoorper i poteri che sono, per entrare e uscire da qualsiasi sistema come desiderato. Credo fermamente che questa scappatoia possa essere sigillata per sempre, eliminando il `TASK_UNINTERUPTIB
Nuuwski,

2
sarebbe per favore condividere il codice?
nuovo il

Risposte:


198

Un processo ininterrotto è un processo che si trova in una chiamata di sistema (funzione kernel) che non può essere interrotto da un segnale.

Per capire cosa significhi, è necessario comprendere il concetto di una chiamata di sistema interrompibile. L'esempio classico è read(). Questa è una chiamata di sistema che può richiedere molto tempo (secondi) poiché può potenzialmente comportare la rotazione di un disco rigido o lo spostamento delle testine. Durante la maggior parte di questo tempo, il processo verrà sospeso, bloccando l'hardware.

Mentre il processo è in pausa nella chiamata di sistema, può ricevere un segnale asincrono Unix (diciamo, SIGTERM), quindi si verifica quanto segue:

  • Il sistema chiama si chiude prematuramente ed è impostato per restituire -EINTR allo spazio utenti.
  • Il gestore del segnale viene eseguito.
  • Se il processo è ancora in esecuzione, ottiene il valore restituito dalla chiamata di sistema e può ripetere la stessa chiamata.

Il ritorno anticipato dalla chiamata di sistema consente al codice spazio utente di modificare immediatamente il suo comportamento in risposta al segnale. Ad esempio, terminando in modo pulito in reazione a SIGINT o SIGTERM.

D'altra parte, alcune chiamate di sistema non possono essere interrotte in questo modo. Se il sistema chiama le bancarelle per qualche motivo, il processo può rimanere indefinitamente in questo stato invendibile.

LWN ha pubblicato un bell'articolo che ha toccato questo argomento a luglio.

Per rispondere alla domanda originale:

  • Come evitare che ciò accada: capire quale driver ti sta causando problemi e smettere di usarlo o diventare un hacker del kernel e risolverlo.

  • Come terminare un processo ininterrotto senza riavviare: in qualche modo far terminare la chiamata di sistema. Spesso il modo più efficace per farlo senza premere l'interruttore di alimentazione è quello di tirare il cavo di alimentazione. Puoi anche diventare un hacker del kernel e fare in modo che il driver usi TASK_KILLABLE, come spiegato nell'articolo LWN.


31
Ho tirato il cavo di alimentazione sul mio laptop e non funziona, purtroppo. ;-)
thararpy,

1
Non è EINTR invece di EAGAIN? Inoltre read () restituisce -1 ed errno è impostato sull'errore.
Lethalman,

2
@Dexter: ti manca davvero il punto. Leggi l'articolo LWN : lwn.net/Articles/288056 . Tali problemi sono causati da programmatori di driver di dispositivo pigri e devono essere risolti nel codice del driver di dispositivo.
ddaa,

4
@ddaa "La tradizione Unix (e quindi quasi tutte le applicazioni) ritiene che le scritture di file store non siano interrompibili per il segnale. Non sarebbe sicuro o pratico cambiare questa garanzia." -> Questa è esattamente la parte più sbagliata di tutto questo IMO. Basta interrompere la richiesta di lettura / scrittura del driver e quando il dispositivo reale (disco rigido / scheda di rete / ecc.) Consegna i dati, ignorarli. Un kernel del sistema operativo dovrebbe essere creato in modo tale che nessuno sviluppatore possa rovinarlo.
Dexter,

2
@ddaa So che Linux non è un microkernel, anche se non sono sicuro di quale parte del mio commento sia correlata ad esso ... E quindi, il tuo commento significa che un sistema operativo microkernel non ha problemi con quei processi "ininterrotti"? Perché se così non fosse, forse è tempo per me di diventare un fan del microkernel ...: D
Dexter

49

Quando un processo è in modalità utente, può essere interrotto in qualsiasi momento (passando alla modalità kernel). Quando il kernel torna in modalità utente, controlla se ci sono segnali in sospeso (compresi quelli che vengono usati per terminare il processo, come SIGTERMe SIGKILL). Ciò significa che un processo può essere interrotto solo al ritorno in modalità utente.

Il motivo per cui un processo non può essere interrotto in modalità kernel è che potrebbe potenzialmente danneggiare le strutture del kernel utilizzate da tutti gli altri processi nella stessa macchina (allo stesso modo l'uccisione di un thread può potenzialmente danneggiare le strutture dati utilizzate da altri thread nello stesso processo) .

Quando il kernel ha bisogno di fare qualcosa che potrebbe richiedere molto tempo (aspettando su una pipe scritta da un altro processo o aspettando che l'hardware faccia qualcosa, per esempio), dorme contrassegnandosi come sleep e chiamando lo scheduler per passare a un altro processo (se non esiste un processo non inattivo, passa a un processo "fittizio" che dice alla CPU di rallentare un po 'e si trova in un ciclo - il ciclo inattivo).

Se un segnale viene inviato a un processo di sospensione, deve essere svegliato prima che ritorni nello spazio utente e quindi elabori il segnale in sospeso. Qui abbiamo la differenza tra i due principali tipi di sonno:

  • TASK_INTERRUPTIBLE, il sonno interrompibile. Se un'attività è contrassegnata con questa bandiera, sta dormendo, ma può essere svegliata dai segnali. Ciò significa che il codice che ha contrassegnato l'attività come inattiva si aspetta un segnale possibile e dopo che si è svegliato lo controlla e torna dalla chiamata di sistema. Dopo che il segnale è stato gestito, la chiamata di sistema può essere potenzialmente riavviata automaticamente (e non entrerò nei dettagli su come funziona).
  • TASK_UNINTERRUPTIBLE, il sonno ininterrotto. Se un'attività è contrassegnata con questo flag, non si aspetta che venga svegliata da qualcosa di diverso da quello che sta aspettando, sia perché non può essere riavviata facilmente, sia perché i programmi si aspettano che la chiamata di sistema sia atomica. Questo può essere usato anche per dormire noti per essere molto brevi.

TASK_KILLABLE (menzionato nell'articolo LWN collegato alla risposta di ddaa) è una nuova variante.

Questo risponde alla tua prima domanda. Per quanto riguarda la tua seconda domanda: non puoi evitare di dormire ininterrottamente, sono una cosa normale (succede, ad esempio, ogni volta che un processo legge / scrive da / sul disco); tuttavia, dovrebbero durare solo una frazione di secondo. Se durano molto più a lungo, di solito significa un problema hardware (o un problema del driver del dispositivo, che sembra lo stesso al kernel), in cui il driver del dispositivo è in attesa che l'hardware faccia qualcosa che non accadrà mai. Può anche significare che stai usando NFS e il server NFS è inattivo (è in attesa che il server si ripristini; puoi anche usare l'opzione "intr" per evitare il problema).

Infine, il motivo per cui non è possibile ripristinare è lo stesso motivo che il kernel attende fino al ritorno in modalità utente per inviare un segnale o interrompere il processo: potrebbe corrompere potenzialmente le strutture di dati del kernel (il codice in attesa di un sonno interrompibile potrebbe ricevere un errore che lo dice per tornare allo spazio utente, dove il processo può essere interrotto; il codice in attesa di un sonno ininterrotto non prevede alcun errore).


1
Anche il bug di blocco del filesystem è una causa probabile, l'IME.
Tobu

3
Non capisco tutto questo. "non è possibile evitare i sonno ininterrotti" - il sistema operativo non può essere realizzato in modo tale che il sonno ininterrotto semplicemente NON ESISTE come stato? Quindi la parte relativa alla corruzione - la parte in modalità kernel del processo stesso (o qualunque cosa POTREBBE causare la corruzione) non può essere terminata o solo il suo codice modificato direttamente in memoria per tornare? Spiega perché è così difficile / impossibile farlo che nemmeno Linux lo ha fatto. (Pensavo che questo problema esistesse solo su Windows)
Dexter,

L'unico caso a cui riesco a pensare che renderebbe (in modo sicuro) uccidere quei processi davvero impossibili (e non solo, diciamo, eccezionalmente difficile) è se l'hardware stesso potrebbe causare la corruzione. L'hardware non può essere controllato; il kernel può . Ma è il kernel che ottiene i dati dall'hardware e modifica la memoria (ecco perché non deve essere liberato prima che il processo ritorni in modalità utente e perché potrebbe verificarsi il danneggiamento) ... cambia il codice del kernel in memoria e non ci sono più problemi.
Dexter,

@Dexter pensa al kernel come se fosse un singolo processo multi-thread, in cui la parte in modalità kernel di ogni processo è un thread all'interno del kernel. Il tuo suggerimento sarebbe negativo come uccidere un singolo thread in un programma multi-thread: potrebbe lasciare blocchi penzolanti, strutture di dati temporaneamente modificate o nel mezzo di modifiche, e così via.
CesarB,

@CesarB hai ragione sull'uccidere un thread ... Ma il thread "principale" (che sarebbe il kernel del sistema operativo e altri thread sarebbero driver ad esempio) in qualche modo gestirlo? Anche se quelle strutture "nel mezzo di essere modificate" sembrano essere un problema davvero difficile ... forse non vedremo mai un sistema operativo in cui i processi ininterrotti sarebbero impossibili :(
Dexter

23

I processi ininterrotti normalmente stanno aspettando l'I / O a seguito di un errore di pagina.

Considera questo:

  • Il thread tenta di accedere a una pagina che non è nel core (o un eseguibile che è caricato su richiesta, una pagina di memoria anonima che è stata scambiata, o un file mmap () che è caricato su richiesta, che sono molto stessa cosa)
  • Il kernel ora sta (cercando di) caricarlo
  • Il processo non può continuare fino a quando la pagina non è disponibile.

Il processo / task non può essere interrotto in questo stato, perché non può gestire alcun segnale; in tal caso, si verificherebbe un altro errore di pagina e sarebbe tornato dove si trovava.

Quando dico "process", intendo davvero "task", che sotto Linux (2.6) si traduce approssimativamente in "thread" che può avere o meno una singola voce "gruppo di thread" in / proc

In alcuni casi, potrebbe essere in attesa da molto tempo. Un tipico esempio di ciò sarebbe dove il file eseguibile o mmap'd si trova su un filesystem di rete in cui il server non è riuscito. Se l'I / O alla fine ha esito positivo, l'attività continuerà. Se alla fine fallisce, l'attività otterrà generalmente un SIGBUS o qualcosa del genere.


1
Se alla fine fallisce, l'attività otterrà generalmente un SIGBUS o qualcosa del genere. Aspetta, il kernel non può essere creato in modo tale che, quando si uccidono quei processi "ininterrotti", DIAMO semplicemente che l'operazione di I / O non è riuscita? Quindi il processo tornerebbe in modalità utente e sparirebbe? Deve esserci un modo per uccidere in modo sicuro quei processi di stato "D". Immagino che non sia facile ed è per questo che né Windows né Linux hanno ancora questa possibilità. D'altra parte, vorrei essere in grado di uccidere quei processi almeno in modo non sicuro. Non mi interessa il possibile arresto anomalo del sistema o altro ...
Dexter,

@Dexter hmm, non ho mai avuto questo problema con Windows. Qual è un modo per riprodurlo lì? Almeno secondo questo post , tutte le richieste I / O possono essere interrotte in Windows.
Ruslan,

1

Alla tua terza domanda: penso che tu possa uccidere i processi ininterrotti eseguendo sudo kill -HUP 1. Riavvia init senza terminare i processi in esecuzione e dopo averlo eseguito, i miei processi ininterrotti erano spariti.


-3

Se stai parlando di un processo "zombie" (che è designato come "zombie" nell'output ps), allora questo è un record innocuo nell'elenco dei processi in attesa che qualcuno raccolga il suo codice di ritorno e potrebbe essere tranquillamente ignorato.

Potresti descrivere cosa e "processo ininterrotto" fa per te? Sopravvive alla "uccisione -9" e allegria felicemente? Se questo è il caso, allora è bloccato su alcuni syscall, che sono bloccati in alcuni driver, e sei bloccato con questo processo fino al riavvio (e talvolta è meglio riavviare presto) o scaricare il driver pertinente (che è improbabile che accada) . Potresti provare a usare "strace" per scoprire dove è bloccato il tuo processo ed evitarlo in futuro.


I driver non possono essere scaricati forzatamente nello stesso modo in cui un processo potrebbe essere ucciso? So che la modalità kernel ha un accesso più privilegiato rispetto alla modalità utente, ma non può mai essere più privilegiata del sistema operativo stesso. Qualsiasi cosa in esecuzione in modalità kernel può sempre manomettere qualsiasi altra esecuzione in modalità kernel - semplicemente non c'è controllo.
Dexter,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.