Perché ps * molto * occasionalmente non riesce a trovare un processo valido?


9

Ho riscontrato un problema strano in cui un ps -o args -p <pid>comando molto occasionalmente non riesce a trovare il processo in questione, anche se è sicuramente in esecuzione sul server in questione. I processi in questione sono script wrapper di lunga durata utilizzati per avviare alcune app Java.

Le occorrenze "in the wild" del problema sembrano sempre accadere nelle prime ore del mattino, per cui v'è qualche evidenza che è legato al carico del disco sul server in questione, perché sono piuttosto pesantemente caricati poi, ma eseguendo il psin domanda in un ciclo stretto, alla fine posso replicare il problema - una volta ogni poche centinaia circa di giri ottengo un errore.

Eseguendo il seguente script bash, sono riuscito a generare l'output di strace sia per una corsa fallita che per una riuscita:

while [ $? == 0 ] ; do strace -o fail.out ps -o args -p <pid> >/dev/null ; done ; strace -o good.out ps -o args -p <pid>

Confrontando l'output da fail.oute good.out, posso vedere che la getdentschiamata di sistema in esecuzione che fallisce in qualche modo restituisce un numero molto più piccolo rispetto al conteggio effettivo dei processi sul sistema (nell'ordine di ~ 500 rispetto a ~ 1100)

grep getdents good.out
  getdents(5, /* 1174 entries */, 32768)  = 32760
  getdents(5, /* 31 entries */, 32768)    = 992
  getdents(5, /* 0 entries */, 32768)     = 0

grep getdents fail.out
  getdents(5, /* 673 entries */, 32768)   = 16728
  getdents(5, /* 0 entries */, 32768)     = 0

... e quell'elenco più breve non include il pid in questione, quindi non è stato trovato.

Puoi ignorare questa sezione, gli errori ENOTTY sono spiegati dal commento di dave_thompson di seguito e non sono correlati

Inoltre, la corsa fallita ottiene alcuni ENOTTYerrori che non compaiono nella corsa riuscita. Vedo all'inizio dell'output che vedo

ioctl (1, TIOCGWINSZ, 0x7fffe19db310) = -1 ENOTTY (ioctl inappropriato per dispositivo) ioctl (1, TCGETS, 0x7fffe19db280) = -1 ENOTTY (ioctl inappropriato per dispositivo)

E alla fine ne vedo uno singolo

ioctl (1, TCGETS, 0x7fffe19db0d0) = -1 ENOTTY (ioctl inappropriato per il dispositivo)

Il fallimento ioctlalla fine si verifica subito prima dei psritorni, ma si verifica dopo che psha già stampato un set di risultati vuoto, quindi non sono sicuro che siano correlati. So che sono coerenti in tutti gli output di strace falliti che ho, ma non compaiono in quelli di successo.

Non ho assolutamente idea del perché di getdentstanto in tanto non trovo l'elenco completo dei processi, e ora ho raggiunto il punto in cui ho intenzione di schiaffeggiare un cerotto sull'intera cosa cambiando lo script di controllo che controlla lo script wrapper in questione di chiamare la psseconda volta se la prima fallisce, ma sarei interessato a sapere se qualcuno ha qualche idea di cosa sta succedendo qui.

Il sistema in questione esegue il kernel 4.16.13-1.el7.elrepo.x86_64 su CentOS 7 e procps-ng versione 3.3.10-17.el7_5.2.x86_64


1
Cordiali saluti, gli ioctl hanno a che fare con le impostazioni del terminale (ad esempio, il primo è quello di trovare il numero di righe e colonne) - quindi è strano che non riescano, ma probabilmente non è una causa diretta. Sembra un bug del kernel ...
derobert


2
Hai >/dev/nulll'invocazione "fallita" (nel ciclo) ma non l'invocazione "buona", quindi l'ENOTTY del 1 °
f.

Oh dannazione. Grazie per aver catturato quel Dave, questo spiega sicuramente gli ENOTTY.
James,

Sono contento di vedere che non sono l'unico ad avere questo problema. Il modo in cui mi aggiro è avere un tentativo-catch che riproverà se il comando non riesce, comunque fastidioso: /
Josh Correia

Risposte:


7

Prendi in considerazione la possibilità di leggere le informazioni necessarie direttamente dal /procfilesystem anziché tramite uno strumento come ps. Troverai le informazioni che stai cercando ("args") all'interno del file /proc/$pid/cmdline, separate solo da byte NUL anziché da spazi.

È possibile utilizzare questo sedone-liner per ottenere gli argomenti del processo $pid:

sed -e 's/\x00\?$/\n/' -e 's/\x00/ /g' "/proc/$pid/cmdline"

Questo comando è equivalente a:

ps -o args= -p "$pid"

(Usando args=in pssi omette l'intestazione.)

Il sedcomando cercherà prima l'ultimo byte NUL finale e lo sostituirà con una nuova riga, dopodiché sostituirà tutti gli altri byte NUL (separando i singoli argomenti) con spazi, producendo infine lo stesso formato da cui stai vedendo ps.


Per quanto riguarda l'elenco dei processi nel sistema, lo psfa elencando le directory /proc, ma ci sono condizioni di gara intrinseche a quella procedura, poiché i processi si avviano ed escono mentre psè in esecuzione, quindi quello che ottieni non è in realtà un'istantanea ma un'approssimazione. In particolare, è possibile che psmostrerà i processi che sono già terminati quando mostra i suoi risultati o omette i processi che sono stati avviati mentre era in esecuzione (ma non sono stati restituiti dal kernel mentre elencano i contenuti di /proc.)

Ho sempre pensato che se un processo è lì davanti psinizia e ancora lì dopo il gioco è fatto, allora sarebbe non perdere per esso, ho assunto il kernel garantirebbe chi sarebbe sempre incluso, anche se c'è un sacco di zangola di altri processi essere creato e distrutto. Ciò che stai descrivendo implica che non è così. Sono ancora scettico su questo, ma dato che ci sono condizioni di gara conosciute nel modo in cui psfunziona, immagino sia almeno plausibile che elencare i PID da /procperdere uno esistente a causa di quelle condizioni di gara.

Sarebbe possibile verificarlo controllando l'origine del kernel Linux, ma non l'ho ancora fatto (quindi), quindi non posso davvero dire con certezza se esiste una tale condizione di competizione che mancherebbe un processo di lunga durata, come tu descrivi.


L'altra parte è il modo in cui psfunziona. Anche se gli stai passando un singolo PID con l' -pargomento, elenca comunque tutti i PID esistenti, anche se sei interessato solo a quello singolo. In questo caso potrebbe sicuramente prendere una scorciatoia e saltare l'elenco delle voci /proce andare direttamente a /proc/$pid.

Non posso dire perché sia ​​stato implementato in questo modo. Forse perché la maggior parte delle psopzioni sono "filtri" sui processi, quindi implementare -pnello stesso modo è stato più semplice, prendere una scorciatoia per andare dritto /proc/$pidpotrebbe comportare un percorso di codice separato o la duplicazione del codice ... Un'altra ipotesi è che alcuni casi tra cui -ppiù opzioni aggiuntive sarebbero finisce per richiedere un elenco, quindi è forse complesso determinare quali casi esatti consentirebbero di prendere la scorciatoia e quali no.


Il che ci porta alla soluzione, andando direttamente /proc/$pid, senza elencare l'intero set di PID del sistema, evitando tutte le razze conosciute e semplicemente ottenendo le informazioni di cui hai bisogno direttamente dalla fonte.

È un po 'brutto, ma il problema che descrivi effettivamente esiste, dovrebbe essere un modo affidabile per recuperare tali informazioni.


2
Grazie per quel Filipe, ho effettuato l'upgrade perché il comando sed è utile (e ho modificato i nostri script per guardare semplicemente in / proc) e perché non mi ero reso conto che l'aggiunta di un '=' alla ps avrebbe lasciato cadere l'intestazione . Non ho accettato la risposta perché sono ancora molto curioso di sapere perché non vede l'intero elenco di / proc e sto sperando che qualcun altro lo sappia :)
James
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.