Cos’è un processo ininterrotto?

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 a quando non riavvio il mio computer (anche se logout). Le mie domande sono:

  • Cosa fa diventare un processo ininterrotto?
  • Come faccio a impedire che ciò accada?
  • Questa è probabilmente una domanda stupida, ma esiste un modo per interromperlo senza riavviare il computer?

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

    Per capire cosa significa, è 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 coinvolgere la rotazione di un disco rigido o il movimento di teste. Durante la maggior parte di questo tempo, il processo sarà inattivo, bloccando l’hardware.

    Mentre il processo sta dormendo nella chiamata di sistema, può ricevere un segnale asincrono unix (ad esempio, SIGTERM), quindi accade quanto segue:

    • Le chiamate di sistema escono prematuramente e sono impostate per restituire -EINTR nello spazio utente.
    • Il gestore del segnale viene eseguito.
    • Se il processo è ancora in esecuzione, ottiene il valore di ritorno dalla chiamata di sistema e può ripetere la stessa chiamata.

    Il ritorno anticipato dalla chiamata di sistema consente allo user code code di modificare immediatamente il suo comportamento in risposta al segnale. Ad esempio, terminando in modo pulito in risposta 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 non controllabile.

    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 causa problemi e smettere di usare o diventare un hacker del kernel e risolverlo.

    • Come uccidere un processo ininterrotto senza riavviare: in qualche modo, terminare la chiamata di sistema. Spesso il modo più efficace per farlo senza colpire l’interruttore di alimentazione è tirare il cavo di alimentazione. Puoi anche diventare un kernel hacker e rendere il driver utilizza TASK_KILLABLE, come spiegato nell’articolo LWN.

    Quando un processo è in modalità utente, può essere interrotto in qualsiasi momento (passando alla modalità kernel). Quando il kernel ritorna in modalità utente, controlla se ci sono segnali in sospeso (compresi quelli utilizzati per uccidere il processo, come SIGTERM e SIGKILL ). Ciò significa che un processo può essere ucciso solo al ritorno alla modalità utente.

    La ragione per cui un processo non può essere ucciso in modalità kernel è che potrebbe potenzialmente corrompere le strutture del kernel utilizzate da tutti gli altri processi nella stessa macchina (allo stesso modo uccidere un thread può potenzialmente corrompere le strutture dati utilizzate da altri thread nello stesso processo) .

    Quando il kernel ha bisogno di fare qualcosa che potrebbe richiedere molto tempo (in attesa di una pipe scritta da un altro processo o in attesa che l’hardware faccia qualcosa, ad esempio), dorme marcando se stesso come addormentato e chiamando lo scheduler per passare a un altro processo (se non c’è un processo non-sleep, passa ad un processo “fittizio” che dice alla CPU di rallentare un po ‘e si siede in un ciclo – il ciclo di inattività).

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

    • TASK_INTERRUPTIBLE , il sonno interrompibile. Se un’attività è contrassegnata da questo flag, sta dormendo, ma può essere svegliata dai segnali. Ciò significa che il codice che ha contrassegnato l’attività come addormentato è in attesa di un segnale ansible e, dopo il suo ritriggersrsi, verificherà la presenza e tornerà dalla chiamata di sistema. Dopo che il segnale è stato gestito, la chiamata di sistema può potenzialmente essere riavviata automaticamente (e non entrerò nei dettagli su come funziona).
    • TASK_UNINTERRUPTIBLE , il sonno non TASK_UNINTERRUPTIBLE . Se un’attività è contrassegnata con questo flag, non si prevede di essere ritriggersta da qualcosa che non sia in attesa, sia perché non può essere riavviata facilmente, sia perché i programmi si aspettano che la chiamata di sistema sia atomica. Questo può essere utilizzato anche per i dormi 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 ininterrotta, sono una cosa normale (capita, ad esempio, ogni volta che un processo legge / scrive da / verso il disco); tuttavia, dovrebbero durare solo una frazione di secondo. Se durano molto più a lungo, di solito significa un problema hardware (o un problema con il driver del dispositivo, che è uguale al kernel), dove il driver del dispositivo è in attesa che l’hardware esegua qualcosa che non accadrà mai. Può anche significare che stai usando NFS e che il server NFS non funziona (è in attesa che il server si ripristini, puoi anche usare l’opzione “intr” per evitare il problema).

    Infine, la ragione per cui non è ansible ripristinare è la stessa ragione per cui il kernel attende fino al ritorno alla modalità utente per fornire un segnale o uccidere il processo: potrebbe corrompere le strutture dati del kernel (il codice in attesa di un sonno interrompibile può ricevere un errore che lo informa per tornare allo spazio utente, dove il processo può essere ucciso, codice in attesa su un sonno ininterrotto non si aspetta alcun errore).

    I processi non interrompibili sono in genere in attesa di I / O in seguito a un errore di pagina.

    Considera questo:

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

    Il processo / compito non può essere interrotto in questo stato, perché non può gestire alcun segnale; se così fosse, si sarebbe verificato un altro errore di pagina e sarebbe tornato dov’era.

    Quando dico “process”, intendo veramente “task”, che sotto Linux (2.6) traduce approssimativamente in “thread” che può avere o meno una singola voce “thread group” in / proc

    In alcuni casi, potrebbe essere necessario attendere 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 ha fallito. Se l’I / O riesce, l’attività continuerà. Se alla fine fallisce, l’attività generalmente riceverà un SIGBUS o qualcosa del genere.

    È ansible che un programma possa essere scritto per avviare un processo che va in uno stato TASK_UNINTERUPTIBLE ogni volta che il sistema non è in uno stato di inattività, quindi con la raccolta forzata di dati, in attesa di trasmettere una volta che il super utente è uscito? Questa sarebbe una miniera d’oro per gli hacker per recuperare informazioni, tornare allo stato zombie e trasmettere informazioni attraverso la rete in idle. Alcuni possono sostenere che questo è un modo per creare una Blackdoor per i poteri che sono, per entrare e uscire da qualsiasi sistema come desiderato. Credo fermamente che questa scappatoia possa essere sigillata per sempre, eliminando lo stato TASK_UNINTERUPTIBLE .

    Lo considero un serio ma delicato problema di sicurezza per i sistemi Linux, che hanno una reputazione per la sicurezza, attraverso il potenziamento del superutente. Sto lavorando al mio modo di diventare un Kernel Hacker, tuttavia, penso che ci siano gli hacker del kernel là fuori che possono risolvere questa debacle.

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

    Se stai parlando di un processo “zombie” (che è designato come “zombie” nell’output di ps), allora questo è un record innocuo nella lista dei processi in attesa che qualcuno raccolga il suo codice di ritorno e possa essere tranquillamente ignorato.

    Potresti descrivere cosa e “processo ininterrotto” è per te? Sopravvive al “kill -9” e gongola felicemente? Se questo è il caso, allora è bloccato su alcuni syscall, che è bloccato in alcuni driver, e sei bloccato con questo processo fino al riavvio (e talvolta è meglio riavviare presto) o lo scarico del driver rilevante (che è improbabile che accada) . Potresti provare a usare “strace” per scoprire dove è bloccato il tuo processo ed evitarlo in futuro.