Gestione ”classica” dei processi su GNU/Linux

Dalle basi teoriche, mediante i comandi maggiormente utilizzati, ecco come lanciare, instradare, scovare ed, eventualmente, “uccidere” un processo

Per controllare efficacemente il proprio sistema è indispensabile comprendere fino in fondo il funzionamento dei processi, ovvero le singole esecuzioni di quelle entità che, nel loro complesso e spesso in concerto, erogano tutta la moltitudine di servizi con cui siamo abituati ad operare. Per chiarirci meglio le idee richiameremo alcuni concetti di teoria delle macchine e di Unix. Ogni volta che il sistema operativo, ovvero una sua parte e, quindi, un eseguibile che più o meno volontariamente abbiamo invocato, agisce, vengono manipolate in memoria determinate strutture dati proprie del processo. Alcune strutture contengono costrutti utili alle sincronizzazioni e ai controlli, altre istruzioni da eseguire, altre ancora una porzione di memoria in cui ospitare i risultati delle operazioni. Ciò vale per ogni processo. Esaminando le cose ad un livello ancora più basso, ogni processo può essere ulteriormente suddiviso in uno o più thread che rappresentano una sequenza di operazioni da processare nell’ordine in cui sono lette. Questa concezione seriale del calcolo, derivata dalla progettazione dei calcolatori, è stata superata e in alcuni contesti, come quelli dei processori più moderni, è possibile processare contemporaneamente più thread.

I processori in grado di far ciò vengono definiti multithread; per essere sfruttati in tal senso è nondimeno necessario che tutti i livelli di software coinvolti siano stati concepiti appositamente per lo scopo. Comprendere ciò aiuta a definire meglio alcuni fenomeni altrimenti oscuri, come le cadute prestazionali rispetto le attese osservate adoperando sistemi operativi di concezione obsoleta ma ospitati su macchine di realizzazione più moderna, ed è inoltre utile per fugare la confusione che spesso nasce fra i termini multithread e multitask. Mentre il primo termine fa riferimento, quindi, a principi di parallelismo e a gestione contemporanea del calcolo principalmente dal punto di vista hardware, il secondo si riferisce alla capacità software di interrompere una serie di operazioni per gestirne un’altra, quindi riprendere la prima serie di operazioni, dividendo così la potenza di calcolo a “fette di tempo”, secondo questo modello, per un numero sterminato di sequenze operative, quindi anche di processi. La gestione dei tempi di calcolo e di attesa è controllata dal kernel mediante opportuni algoritmi di scheduling.

Mettiamo momentaneamente da parte questi concetti, ci saranno utili per meglio comprendere quanto segue, per i nostri scopi non sarà necessario, comunque, andare ulteriormente a fondo nell’anatomia dei processi, ma basterà considerare che osservando le cose ad un livello più alto, perché il sistema possa gestire tutti i processi, è necessario che, in memoria, venga mantenuta una mappa globale dei processi attivi. Al livello più alto di astrazione, ovvero a livello dell’utente, ogni processo attivo può essere distinto dagli altri processi attivi tramite il PID (acronimo di Process Identifier), che lo identifica univocamente. Il PID è un numero che viene assegnato ad ogni processo alla sua nascita e che lo accompagna durante tutta la sua esistenza. Se due processi nascono in istanti successivi il più giovane, seguendo una numerazione incrementale, avrà il PID immediatamente seguente a quello del predecessore, a patto che il PID da assegnare sia libero, perché i PID, pur essendo numerati in sequenza, devono tener conto dei limiti delle attuali architetture. Essendo impossibile gestire numeri “grandi quanto si vuole” potrà capitare che il PID da assegnare richieda un numero più grande di quello gestibile, in questo caso, semplicemente, l’assegnazione ricomincia dall’inizio, sfruttando e riassegnando però solo PID dei processi che nel frattempo sono morti. Conoscendo il PID sono possibili tutta una serie di operazioni che altrimenti risulterebbero quantomeno difficilmente praticabili. Spesso, per intervenire su un processo, è necessario conoscere questo numero da passare come argomento.

Gli stati dei processi

Da “dormiente” a “zombie”. Cosa significa?
Un processo può essere runnable o running, vale a dire pronto per essere eseguito o in esecuzione, oppure sleeping, ovvero in attesa di poter operare su una risorsa come il disco o di un evento come l’input da tastiera; in questo stato il processo non consuma risorse di calcolo. Un processo può divenire stopped, cioè interrotto da uno specifico segnale e quindi in attesa del segnale di continuazione, oppure zombie, cioè in attesa di essere correttamente terminato.

La directory  proc

Da qui il kernel comunica con l’esterno.
Una delle cose fondamentali da imparare è la diagnostica legata ai processi. Esiste una gestione molto raffinata del carico di calcolo sotto GNU/Linux. L’utente più smaliziato può monitorare ogni esecuzione indagandola e, se necessario, instradandola. Per i curiosi consigliamo di dare un rapido sguardo al percorso di file system /proc. Non è una vera directory appartenente al disco ma una directory virtuale che “fotografa” i processi in esecuzione e colleziona alcune informazioni sulla macchina.

Il Kernel e i processi

Quando il kernel gestisce un processo opera su diverse informazioni. La mappa dello spazio di indirizzamento del processo, i dati riguardanti le risorse da questo utilizzate, la sua maschera dei segnali, il proprietario, rispettivamente l’user UID e il gruppo GID, nonché le loro varianti estese EUID e EGID, utilizzate per meglio modulare le politiche di proprietà e i relativi permessi. Oltre a questo il kernel gestisce lo stato corrente del processo (dormiente, bloccato, eseguibile, ecc.) , informazione intimamente legata alle necessità di schedulazione. Inoltre, considera la priorità di esecuzione del processo (il valore di niceness) ovvero la priorità di scheduling di un processo, questo è il parametro principale utilizzabile per influenzare la quantità di tempo-CPU resa disponibile per il processo. L’algoritmo che presiede lo scheduling tiene inoltre conto del tempo già speso per il processo e del tempo di attesa per l’esecuzione. Il valore di niceness può essere incrementato dall’utente per rendere il processo meno “affamato” di risorse ma mai decrementato. L’utente root può incrementare o decrementare il valore di niceness di qualsiasi processo. Un processo neonato eredita il valore di niceness del padre. Tipicamente, perché un nuovo processo possa nascere, il processo che lo genera dovrà effettuare un fork, generando una copia esatta di se stesso da differenziare in seguito, di norma la niceness non viene però modificata in questa fase. Al boot vengono avviati numerosi processi, sotto GNU/Linux, a meno di particolari personalizzazioni, il primo processo generalmente è init che assumerà PID 1 e avvierà gli altri processi. È necessario intervenire sul niceness di un processo in pochi casi, gli algoritmi di scheduling sono di consueto molto efficienti. Quasi sempre potremo fidarci in tal senso del sistema. Può, comunque, essere una buona idea intervenire sul valore di niceness di un processo se questo, in modo inatteso, assorbe una quantità tale di risorse da rendere difficoltoso il controllo della macchina ma, nonostante ciò, non desideriamo comunque fermarne l’esecuzione.

Monitorare i processi con top

Facile da usare, semplice da capire e affidabilissimo.

Top dice tutto

Comandi di gestioneIl comando top è sicuramente tra i tool migliori per osservare i principali processi in esecuzione e campionarne, a breve intervalli di tempo, alcuni parametri, come, fra gli altri, percentuale di utilizzo per le CPU e la memoria, comando e utente di lancio e, molto importante, il PID associato.

Controllo a tempo Top può filtrare i processi in base agli utenti proprietari, basta premere u. Inoltre, premendo il tasto r è possibile effettuare il renice, quindi impostare una nuova priorità per il processo. Attraverso la pressione di k è poi, invece, possibile uccidere un processo specificandone il PID.

Processi in backgroundTop effettua i controlli a intervalli di tempo. Per stabilire ogni quanto tempo deve farlo premiamo s o d. Inoltre, è meglio evitare intervalli troppo brevi, sia perché appesantiscono il PC, sia perché qualora la schermata cambi troppo rapidamente il risultato risulterebbe di difficile lettura.

Sono in esecuzione, ma non si vedono
Come utile curiosità, ricordiamo anche l’operando & che, accodato a un comando, lo esegue in background restituendo il controllo alla console, e il comando nohup che lancia il comando argomento immune dalla disconnessione consentendo così di affrancare il processo figlio dalla sorte che toccherà al processo padre qualora esso dovesse morire, quindi il processo immune da disconnessione, alla morte del padre, invece di cadere, diventerà figlio del PID 1.

Un processo “Gentile”

I comandi nice e renice per stabilire le priorità
Il valore di niceness di un processo può essere impostato al lancio utilizzando il comando nice e l’eseguibile come argomento. Il comando renice può essere invece utilizzato, passando come argomento il PID, per reimpostare il valore di niceness di un processo attivo. I due comandi nice e renice potrebbero variare nell’uso degli argomenti da sistema a sistema, quindi è buona norma, prima di lanciarli, richiamare sempre le pagine man relative.

Ci sono anche ps, uptime e pstree

Per avere informazioni rapide, è possibile usare questi tre comandi, efficienti e molto potenti.

Processi correnti

Vista ad alberops -ef visualizza l’elenco dei processi attivi segnalando, fra l’altro, il processo padre (PPID), da cui deriva quello corrente e il relativo PID. Inoltre, fornisce informazioni sul tempo di lancio e sul comando invocato per avviare il processo.

Dati temporaliIl comando pstree restituisce una particolare rappresentazione ad albero dei processi in esecuzione e delle relazioni padre-figlio. Passandogli un PID come argomento, l’albero verrà visualizzato partendo da questo.

Kill e i suoi segnaliuptime visualizza il tempo trascorso dall’ultimo avvio e una statistica del peso totale di calcolo in rapporto a quello ottenuto uno, cinque e quindici minuti prima del suo lancio. Spesso questi valori sono espressi come frazione decimale.

Quando desideriamo interrompere, uccidere o far riprendere un processo in esecuzione, dobbiamo lanciargli un segnale. Il comando kill, al contrario di quanto lasciato supporre dal nome molto specifico, può inviare ai processi, con opportuni argomenti, diversi tipi di segnali per ottenere vari risultati. Il segnale di default comporta una richiesta di terminazione software. Ci sono processi che non reagiranno al comando di terminazione software perché alcuni segnali possono essere ignorati, momentaneamente bloccati ovvero accodati in attesa di essere sbloccati, oppure “catturati” e quindi gestiti dal processo qualora quello in questione abbia al suo interno istruzioni per gestire quei segnali. In tutti gli altri casi sarà il kernel ad effettuare azioni volte a soddisfare le richieste effettuate. Il comando man signal fornisce un elenco dei segnali che è possibile inviare. Se un processo non è morto e desideriamo ucciderlo, possiamo provare con il comando kill passando prima del PID argomento -9, così facendo invieremo un segnale di terminazione che non può essere bloccato o catturato.

Ricerche specifiche con pidof e pgrep

Gli operatori appositamente studiati per rispondere a queste esigenze.

Basta il “nome”

Ricerche miratepidof identifica il PID di un processo di cui si conosce solo il nome di lancio, ovvero la riga di comando con cui è stato eseguito, restituendo il valore numerico che ci consente di operare attraverso gli altri tool previsti per il controllo dei processi.

Assassini di massapgrep consente di effettuare diverse ricerche sui PID, sui gruppi che li hanno lanciati, sugli orari di esecuzione e su molto altro ancora, ad esempio pgrep -u user elenca tutti i PID associati all’utente user fornendo una lista dei relativi PID.

Quando più processi sono fuori controllo, possiamo terminarli insieme.

Fuori tutti!

Scova e uccideConoscendo il comando che ha generato più processi, è possibile ucciderli tutti con killall. Questo si rivela utile nel caso in cui, ad esempio, il browser stia generando un elevato numero di istanze e non desideriamo chiuderle tutte una ad una.

pkill effettua ricerche uguali a pgrep, ma uccide i processi trovati. Ricordiamo che solo root può agire su tutti i processi. Il comando pkill può risultare utile quando, ad esempio, bisogna interrompere, in un sol colpo, tutti i processi di un utente.

Leave a Reply

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.