Oggi tmux è crashato. Non un errore, non un segfault, non un bug. Semplicemente: sparito. Tutte le finestre, tutte le sessioni, tutti i pane con le loro Claude che lavoravano — via. Come se qualcuno avesse staccato la spina di un palazzo intero perché un inquilino al terzo piano aveva lasciato il gas aperto.
La cosa interessante è che il server stava bene. Efesto — la macchina Hetzner con 32 giga di RAM e un NVMe che vola — non aveva nessun problema visibile. Nginx serviva pagine, MySQL rispondeva, i cron giravano. Eppure tmux era morto, e con lui tutto il lavoro in corso.
Per capire cosa è successo — davvero, non la versione da Stack Overflow — bisogna scendere sotto il cofano. E una volta che ci sei, scopri un mondo che hai sempre usato senza mai guardare. Come un meccanico che guida ogni giorno ma non ha mai aperto un motore.
Questo articolo è quel viaggio. Non un manuale, non una lista di comandi. È la storia di cosa succede dentro la tua macchina Linux dal momento in cui premi il tasto di accensione a quando scrivi ls e vedi una lista di file. E soprattutto, è la storia di perché tmux è crollato.
Partiamo da qui, perché è dove si nasconde il colpevole del nostro crash.
Quando ti connetti via SSH a un server, non stai semplicemente "entrando". Stai innescando una catena di eventi che coinvolge almeno sei componenti diversi del sistema operativo. Vediamoli uno per uno.
SSH accetta la connessione. Il demone sshd, che gira come root e ascolta sulla porta 22, riceve la tua richiesta. Verifica la chiave pubblica (o la password), e se tutto torna, fa una cosa cruciale: chiede a PAM di creare una sessione.
PAM (Pluggable Authentication Modules) è il buttafuori del sistema. Non è un singolo programma, è un framework — una catena di moduli che vengono eseguiti in sequenza. Uno verifica la password, uno controlla se l'account è scaduto, uno imposta i limiti di risorse, uno scrive nel log che ti sei connesso. PAM è il motivo per cui puoi autenticarti con password, chiave SSH, LDAP, impronta digitale o token hardware usando lo stesso login — i moduli sono intercambiabili.
systemd-logind entra in gioco. Quando PAM conferma che sei legittimo, systemd-logind crea una sessione. Non una sessione nel senso web del termine — una sessione nel senso di "unità di lavoro di un utente". Ha un ID (tipo c29), appartiene a un utente, e viene tracciata. Se fai loginctl list-sessions, le vedi tutte.
Ma ecco la parte cruciale, quella che ha ammazzato tmux oggi.
Il user manager. La prima volta che fai login, systemd non si limita a creare una sessione. Avvia un intero user manager — una istanza di systemd dedicata a te, user@1001.service (dove 1001 è il tuo UID). Questo user manager è il tuo maggiordomo personale: gestisce i servizi che girano sotto il tuo utente, il tuo dbus (il sistema di messaggistica tra processi), e il tuo slice.
Uno slice è un contenitore di risorse. Pensa a un condominio: il sistema è il palazzo, ogni utente ha il suo appartamento (slice), e dentro l'appartamento ci sono le stanze (le sessioni, i servizi). Lo slice di giobi si chiama user-1001.slice, e tutto quello che giobi esegue — tmux, Claude, script Python, qualsiasi cosa — gira dentro questo slice.
E qui arriviamo al punto.
Linger. Per default, systemd ragiona così: "Se l'utente non ha più sessioni attive, non gli serve il user manager. Lo spengo." Il che significa che se tutte le tue connessioni SSH cadono — anche per un secondo, anche per un microsecondo — systemd ammazza il tuo user manager. E il user manager porta giù dbus. E dbus porta giù tutto quello che dipende dalla tua sessione utente. Tmux incluso.
Non è un bug. È il comportamento di design. Systemd è stato pensato per macchine con tanti utenti che fanno login e logout — tipo un'università. Se uno studente si disconnette, ha senso liberare le sue risorse. Ma su un server dove giri tmux con dentro sessioni persistenti, è una trappola.
La soluzione è una riga: loginctl enable-linger giobi. Con linger abilitato, il tuo user manager parte al boot e resta vivo per sempre, indipendentemente dalle sessioni. Il tuo "appartamento" nel condominio resta con le luci accese anche quando esci.
Ecco cosa è successo oggi: una disconnessione SSH (probabilmente un timeout di rete, niente di drammatico) ha lasciato il tuo utente senza sessioni attive per un istante. Systemd ha fatto il suo lavoro — ha spento il user manager. Tmux è crollato.
Nella pratica:loginctl show-user giobiti mostra tutto sul tuo utente — sessioni attive, slice, stato del linger.loginctl list-sessionsmostra le sessioni correnti. Se vediLinger=nosu un server dove usi tmux, hai una bomba a orologeria.
Ogni cosa che gira su Linux è un processo. Non "quasi tutto" — proprio tutto. Il tuo editor, il web server, il comando ls che hai appena lanciato, persino il kernel ha i suoi processi (quelli tra parentesi quadre in ps aux, tipo [kworker]).
Ogni processo ha un numero: il PID, Process ID. Il primo processo che parte è systemd, PID 1. È il capostipite, l'Adamo dei processi. Da lui discendono tutti gli altri, in una struttura ad albero. Puoi vederla con pstree — è illuminante la prima volta.
I processi nascono in un modo che sembra assurdo finché non lo capisci: per clonazione. Quando vuoi lanciare un nuovo programma, il processo corrente si duplica (fork), creando una copia identica di sé stesso. Poi la copia sostituisce il suo codice con il programma nuovo (exec). È come se per assumere un nuovo impiegato, il capo si sdoppiasse e poi il clone cambiasse faccia e competenze.
Sembra inefficiente? Lo è, un po'. Ma è anche elegantemente semplice: fork + exec sono due operazioni atomiche e ben definite. Il kernel sa esattamente cosa fare in ogni caso. E grazie al copy-on-write (la memoria viene copiata solo quando uno dei due processi la modifica), il fork è quasi gratis in termini di risorse.
Quando scrivi ls nella shell e premi invio, succede esattamente questo:
1. Bash fa fork() — ora ci sono due bash identici.
2. Il figlio fa exec("/usr/bin/ls") — si trasforma in ls.
3. Ls fa il suo lavoro, stampa l'output, esce.
4. Il padre (il bash originale) riceve la notifica che il figlio è morto e torna al prompt.
Quel "riceve la notifica" è importante. Quando un processo muore, non sparisce subito. Resta in uno stato chiamato zombie — è morto, non usa CPU né memoria, ma la sua entry nella tabella dei processi esiste ancora perché il padre deve leggere il suo codice di uscita (con la syscall wait). Solo dopo che il padre ha letto l'exit code, il kernel rimuove lo zombie.
Se il padre non chiama wait — perché ha un bug, o perché è troppo impegnato — lo zombie resta lì per sempre. Li vedi con ps aux | grep Z. Non fanno danni (non usano risorse), ma sono il sintomo di un processo padre che non fa il suo lavoro. Come un genitore che non ritira la pagella.
E se il padre muore prima del figlio? Il figlio diventa un orfano. Ma Linux non lascia orfani per strada — il PID 1 (systemd) li adotta automaticamente. È per questo che se ammazzi un processo padre, i figli non muoiono con lui: vengono reparentati a systemd, che farà il wait quando moriranno.
Questo spiega perché tmux funziona. Quando lanci tmux, crea un server (un processo separato) e un client (quello che vedi). Se chiudi il client (disconnetti SSH), il server resta vivo — è un processo indipendente. Quando ti riconnetti, il nuovo client si attacca al server esistente. Il server non dipende dal client per sopravvivere.
Ma il server tmux dipende dal user manager di systemd. E quello, come abbiamo visto, può essere spento.
Nella pratica:ps auxf(con la f di forest) mostra l'albero dei processi — chi è figlio di chi.pstree -pè ancora più leggibile. Se vedi processi zombie, cerca il padre e capisci perché non sta facendo wait. Se il padre è morto, systemd li pulirà.
I processi non vivono isolati. Si parlano, si coordinano, e soprattutto si ammazzano a vicenda. Lo strumento principale per farlo sono i segnali.
Un segnale è un messaggio asincrono da un processo a un altro. "Asincrono" significa che arriva quando arriva — il processo destinatario viene interrotto da qualsiasi cosa stia facendo per gestire il segnale. È l'equivalente di qualcuno che ti tira una pallina di carta mentre lavori.
I segnali più comuni che incontrerai:
SIGTERM (15) — "Per favore, muori." È il segnale educato. Quando fai kill 1234 senza specificare il segnale, mandi SIGTERM. Il processo può intercettarlo, fare pulizia (chiudere file, salvare stato, disconnettersi dal database), e poi uscire con grazia. È come dire al cameriere "il conto, per favore".
SIGKILL (9) — "Muori. Adesso." Non può essere intercettato, non può essere ignorato, non può essere gestito. Il kernel elimina il processo senza dargli la possibilità di fare nulla. Nessuna pulizia, nessun salvataggio, nessun addio. File temporanei restano lì, lock non vengono rilasciati, connessioni restano aperte. È una fucilata. kill -9 è l'ultima spiaggia, non la prima scelta — eppure la vedi usare ovunque come se fosse la normalità.
SIGHUP (1) — "Il terminale si è disconnesso." Storicamente significava "hang up", come riagganciare il telefono. Quando la tua connessione SSH cade, tutti i processi legati a quel terminale ricevono SIGHUP. Molti demoni (nginx, Apache) lo usano per ricaricare la configurazione: kill -HUP $(pidof nginx) dice a nginx "rileggi nginx.conf senza riavviarti". È come dire al cuoco "cambia il menù ma non chiudere il ristorante".
SIGINT (2) — quello che mandi quando premi Ctrl+C. Il processo può intercettarlo. Per questo alcuni programmi chiedono conferma quando premi Ctrl+C — stanno gestendo SIGINT.
SIGSTOP e SIGCONT — pausa e riprendi. Ctrl+Z nella shell manda SIGSTOP (in realtà SIGTSTP, la versione intercettabile), che congela il processo. fg manda SIGCONT per risvegliarlo. Il processo non sa di essere stato in pausa — per lui il tempo si è fermato e ripreso.
Il bello dei segnali è che i processi possono scegliere come reagire a quasi tutti (tranne SIGKILL e SIGSTOP). Un web server che riceve SIGTERM può decidere di finire le richieste in corso prima di morire. Uno script può intercettare SIGINT per fare pulizia. È un sistema elegante.
Il brutto è che a volte i segnali arrivano a catena e creano disastri. Quando l'OOM killer di oggi ha mandato SIGKILL a dbus, systemd ha reagito mandando segnali a tutti i processi nello slice. Effetto domino.
Nella pratica: Usa semprekill PID(SIGTERM) prima dikill -9 PID(SIGKILL). Se il processo non muore con SIGTERM, chiediti perché — potrebbe avere un bug nel signal handler, o potrebbe essere in uno stato D (uninterruptible sleep, tipo I/O su disco).kill -0 PIDnon manda nessun segnale ma ti dice se il processo esiste ancora.
Ecco un fatto che confonde chiunque la prima volta: i processi non vedono la RAM vera. Mai. Ogni processo vive nella sua memoria virtuale — uno spazio di indirizzi finto, enorme (su 64 bit, 128 terabyte teorici), dove lui è convinto di essere solo al mondo.
Il kernel gestisce la traduzione tra indirizzi virtuali e RAM fisica attraverso la MMU (Memory Management Unit), un chip sulla CPU. Quando il processo accede all'indirizzo 0x7fff12345678, la MMU consulta una tabella (la page table) e traduce quell'indirizzo in una posizione reale nella RAM. Il processo non sa e non gli interessa dove sia fisicamente la sua memoria.
Questo sistema ha un vantaggio enorme: overcommit. Il kernel può promettere più memoria di quanta ne abbia. Se un processo chiede 4GB con malloc, il kernel dice "ok" e gli dà 4GB di spazio virtuale. Ma la RAM fisica viene allocata solo quando il processo effettivamente scrive su quelle pagine. È come un albergo che accetta più prenotazioni delle stanze disponibili, scommettendo che non tutti si presenteranno.
E la maggior parte delle volte funziona. I programmi allocano molta più memoria di quanta ne usino davvero. Ma quando tutti si presentano in albergo la stessa sera... arriva l'OOM Killer.
L'OOM Killer (Out Of Memory Killer) è l'ultimo ricorso del kernel. Quando la RAM fisica è esaurita, lo swap è pieno, e non c'è più niente da liberare, il kernel deve ammazzare qualcuno per sopravvivere. La scelta della vittima segue un punteggio (oom_score) basato su quanto memoria usa il processo, da quanto tempo gira, i suoi privilegi, e un aggiustamento manuale (/proc/PID/oom_score_adj).
Quello che è successo oggi: il tuo user slice usava 14GB. Le istanze di Claude (500MB ciascuna per una dozzina), MySQL, i processi Python — tutto insieme. Il kernel ha visto pressione sulla memoria, ha guardato dentro il tuo slice, e ha scelto dbus come vittima. Perché dbus? Probabilmente perché il suo oom_score era sfavorevole — non usa tantissima memoria, ma il kernel cerca di uccidere processi che liberano abbastanza da risolvere il problema senza ammazzare quelli troppo grossi (che potrebbero essere "importanti").
Il problema è che dbus, pur essendo piccolo, era il tessuto connettivo della tua sessione. Ammazzare dbus per liberare qualche mega è come togliere le fondamenta per vendere i mattoni.
E poi c'è lo swap. Lo swap è una zona del disco che funge da memoria d'emergenza. Quando la RAM è sotto pressione, il kernel sposta le pagine meno usate dalla RAM al disco (swap out). È molto più lento — un accesso alla RAM richiede ~100 nanosecondi, un accesso al disco NVMe richiede ~100 microsecondi, mille volte di più. Ma è meglio di crashare.
Oggi il tuo swap era al 70% (5.5GB su 8GB). Significa che il kernel stava già faticando da un po' — spostava roba avanti e indietro tra RAM e swap (il temuto swapping, che rallenta tutto), e alla fine non è più bastato.
Nella pratica:free -hè il primo comando da guardare. Ma attenzione: "used" non è la memoria "persa" — Linux usa la RAM libera come cache disco (buff/cache), e la libera appena qualcuno la chiede. La colonna "available" è quella che conta davvero: è quanta memoria il kernel può rendere disponibile senza swappare. Per vedere chi mangia cosa:ps aux --sort=-%mem | head -15.
Ti ho parlato di slice, di user manager, di "dentro il tuo slice". Ma cosa sono concretamente?
I cgroup (control groups) sono il meccanismo del kernel per organizzare i processi in gruppi e impostare limiti sulle risorse. Pensa a un organigramma aziendale, ma per i processi. Ogni cgroup può avere limiti su CPU, memoria, I/O disco, e rete.
Systemd usa i cgroup pesantemente. La gerarchia è:
/ (root cgroup)
├── system.slice → servizi di sistema (nginx, mysql, sshd)
├── user.slice → tutti gli utenti
│ ├── user-1001.slice → giobi
│ │ ├── user@1001.service → il tuo user manager
│ │ ├── session-c29.scope → una sessione SSH
│ │ └── tmux-spawn-xxx.scope → un pane di tmux
│ └── user-1002.slice → un altro utente
└── init.scope → systemd stesso
Ogni livello può avere limiti. user-1001.slice potrebbe avere un tetto di 16GB — e se i processi di giobi superano quel tetto, il kernel interviene solo dentro quel slice, senza toccare nginx o mysql. È un sistema di quarantena.
Nel nostro caso, cat /sys/fs/cgroup/user.slice/user-1001.slice/memory.max restituisce max — nessun limite esplicito. Ma il kernel, quando è sotto pressione, guarda comunque dentro gli slice per decidere chi sacrificare. E il tuo slice era il più pesante del palazzo.
La cosa elegante dei cgroup è che sono gerarchici. Se metti un limite sullo slice dell'utente, tutti i processi dell'utente lo rispettano — non puoi aggirarlo lanciando più processi. Il limite è sul contenitore, non sul singolo inquilino.
Ogni pane di tmux che lanci crea un nuovo scope — tmux-spawn-xxx.scope. Ogni sessione SSH è un altro scope. Systemd li traccia tutti, e systemd-cgtop ti mostra in tempo reale chi consuma cosa, organizzato per cgroup. È come top ma per gruppi di processi invece che per processi singoli.
Nella pratica:systemd-cgtopmostra consumo per slice.systemctl status user@1001.servicemostra il tuo user manager e i processi sotto di esso.cat /proc/QUALSIASI_PID/cgroupti dice in quale cgroup sta un processo. Se vuoi proteggere un servizio critico dall'OOM killer:systemctl edit nginxe aggiungiOOMScoreAdjust=-900.
Apri un terminale, scrivi comandi, vedi output. Semplice, no? Sotto, è un labirinto.
Partiamo dalla storia: i TTY (teletype) erano macchine fisiche — telescriventi collegate a un computer via cavo seriale. Avevano una tastiera e una stampante (poi un monitor). Ogni TTY era un dispositivo hardware con il suo cavo. Se guardi /dev/tty1 fino a /dev/tty6 sul tuo server, quelli sono i terminali "virtuali" che emulano quelle vecchie telescriventi — li raggiungi con Ctrl+Alt+F1-F6 se hai un monitor fisico attaccato.
Ma tu ti connetti via SSH. E SSH non ha una telescrivente fisica. Allora il kernel crea una PTY (pseudo-terminal) — una coppia di dispositivi virtuali: un master e uno slave. Lo slave (/dev/pts/4, per esempio) si comporta esattamente come un terminale vero dal punto di vista dei programmi — bash, vim, qualsiasi cosa non distingue un PTY da un TTY hardware. Il master è il lato "fuori", collegato a sshd.
Quando scrivi un carattere:
Il tuo client SSH locale lo manda via rete → sshd lo riceve → lo scrive sul master del PTY → il kernel lo passa allo slave → bash lo legge. L'output fa il percorso inverso. È un tubo a due bocche.
I numeri pts/0, pts/1, pts/50 che vedi con who o ps sono questi pseudo-terminali. Ogni connessione SSH ne crea uno nuovo. Ecco perché vedevi pts/12, pts/50, pts/92 nei processi Claude — ogni pane di tmux ha il suo PTY.
Tmux aggiunge un altro livello. Il server tmux crea le sue PTY per ogni pane — tu ti connetti al client tmux, che è collegato al PTY della tua sessione SSH, e il client fa da ponte verso il server, che ha le sue PTY interne. È PTY su PTY. Russian dolls.
Perché importa? Perché il terminale non è solo un tubo passivo. Ha una disciplina di linea (line discipline) — un layer nel kernel che interpreta i caratteri speciali. Ctrl+C non arriva a bash come "Ctrl+C": la line discipline lo intercetta e manda SIGINT al processo in foreground. Ctrl+Z manda SIGTSTP. Ctrl+D non è un segnale — è EOF (End Of File), e la line discipline lo comunica come "fine dell'input".
Anche le dimensioni del terminale sono gestite qui. Quando ridimensioni la finestra, il terminale manda un segnale SIGWINCH ai processi, che possono riadattare il layout. È per questo che vim si riaggiusta quando cambi la dimensione della finestra — sta intercettando SIGWINCH.
Nella pratica:ttyti dice su quale terminale sei.whomostra chi è connesso su quale pts.stty -amostra tutti i settaggi della tua line discipline — incluso quale carattere fa cosa (ctrl+c = intr, ctrl+z = susp). Se un terminale impazzisce (caratteri strani, no echo),resetostty sanelo rimette a posto.
Ti connetti con ssh efesto e sei dentro. Ma "dentro" cosa, esattamente?
La prima connessione SSH è la più complicata. Il client e il server negoziano un algoritmo di crittografia, poi fanno uno scambio di chiavi Diffie-Hellman — un trucco matematico per cui due parti possono concordare un segreto condiviso parlando su un canale pubblico, senza che nessuno che ascolta possa derivare lo stesso segreto. Da quel momento, tutto il traffico è cifrato.
Poi viene l'autenticazione. Con le chiavi (il modo giusto): il server manda una sfida cifrata con la tua chiave pubblica, tu rispondi decifrandola con la privata. Nessuna password transita sulla rete. La chiave privata non lascia mai la tua macchina.
Il file ~/.ssh/config è il coltellino svizzero sottovalutato. Invece di scrivere ssh -i ~/.ssh/id_ed25519 -p 2222 giobi@efesto.giobi.com, definisci:
Host efesto
HostName efesto.giobi.com
User giobi
IdentityFile ~/.ssh/id_ed25519
E scrivi ssh efesto. Fine.
Perché SSH droppa? La connessione SSH è un tunnel TCP persistente. Se nessun dato transita per un po', i router/firewall nel mezzo possono decidere di chiudere la connessione (timeout NAT). Le opzioni ServerAliveInterval 60 e ServerAliveCountMax 3 nel config mandano un keepalive ogni 60 secondi — se tre consecutivi falliscono, il client sa che la connessione è morta e si disconnette pulitamente invece di restare appeso.
E qui torniamo al nostro problema. Una disconnessione SSH senza keepalive configurato può restare "appesa" lato server per minuti, durante i quali il server pensa che tu sia ancora connesso. Ma se il tuo client si riconnette e il server nel frattempo ha fatto timeout sulla vecchia connessione, c'è un momento in cui hai zero sessioni attive. Se linger è disattivato, è in quel momento che systemd spegne il tuo user manager.
Nella pratica: MettiServerAliveInterval 60eServerAliveCountMax 3nel tuo~/.ssh/config— sia sul client locale che in/etc/ssh/sshd_configconClientAliveInterval. Usassh -v(verbose) per debuggare problemi di connessione — mostra ogni passo della negoziazione. Se SSH è lento a connettersi, spesso è un DNS reverse lookup che fallisce:UseDNS noin sshd_config.
Sembra la cosa più banale del mondo. Ma il viaggio di ls dalla tastiera al terminale tocca quasi tutto quello che abbiamo visto finora.
Input. Premi i tasti l, s, invio. Ogni tasto viaggia via SSH dal tuo client al server, attraverso il PTY, la line discipline del kernel accumula i caratteri. Bash li vede solo quando premi invio — la line discipline fa il buffering per te, e gestisce anche il backspace, le frecce, la history.
Parsing. Bash riceve la stringa "ls". Prima controlla se è un builtin (un comando interno di bash, tipo cd, echo, export). ls non lo è — è un programma esterno. Allora bash cerca l'eseguibile nel PATH.
Il PATH è una variabile d'ambiente — una lista di directory separate da :. Bash le scorre in ordine finché non trova un file chiamato ls che sia eseguibile. Sul tuo server: /usr/bin/ls. Se scrivi un comando che non è nel PATH, bash dice "command not found" — non perché il comando non esiste, ma perché non è in nessuna delle directory elencate.
Fork + Exec. Bash chiama fork() — si clona. Il clone chiama execve("/usr/bin/ls", ...) — il kernel carica il binario di ls in memoria. Ma "caricare in memoria" non significa leggere tutto il file da disco. Il kernel usa il memory mapping: mappa il file nella memoria virtuale del processo, e carica le pagine effettive solo quando servono (demand paging). Se ls ha 100 funzioni ma ne usa 3, solo quelle 3 vengono caricate.
Syscall. Ls vuole leggere il contenuto della directory corrente. Non può farlo direttamente — nessun processo può toccare il disco senza passare dal kernel. Ls chiama la syscall getdents (get directory entries), che è una richiesta formale al kernel: "dammi la lista dei file in questa directory". Il kernel accede al filesystem, legge gli inode, e restituisce i risultati.
Le syscall sono il confine tra userspace (dove girano i tuoi programmi) e kernelspace (dove gira il kernel). Un programma non può fare quasi nulla da solo — leggere file, scrivere output, allocare memoria, comunicare via rete — tutto passa per le syscall. Sono l'API del kernel. Ce ne sono circa 300 su Linux, e strace ls te le mostra tutte in tempo reale — è ipnotico.
Output. Ls formatta i risultati e chiama write() — un'altra syscall — per mandare il testo al file descriptor 1 (stdout). Il kernel prende quel testo e lo manda al PTY, che lo passa attraverso la line discipline, che lo manda al master del PTY, che lo manda a sshd, che lo cifra e lo manda via rete al tuo client, che lo renderizza sullo schermo.
Exit. Ls termina con exit(0) — il codice 0 significa "tutto ok". Il kernel libera le risorse del processo, lo mette in stato zombie, e manda SIGCHLD a bash (il padre). Bash fa wait(), legge l'exit code, e torna al prompt. Se l'exit code fosse diverso da 0, bash lo salva in $? — ecco perché echo $? dopo un comando ti dice se è andato bene o male.
Tutto questo per tre lettere e un invio. Ogni singola volta.
Nella pratica:strace -f comandomostra tutte le syscall di un comando (la-fsegue anche i figli).which lsti dice quale eseguibile bash userebbe.type lsè meglio: ti dice se è un alias, un builtin, o un programma esterno.echo $PATHmostra il tuo PATH — se un comando "non si trova", il 90% delle volte il problema è qui.
Quel crash di tmux ti ha insegnato più di quanto avrebbe fatto un corso. Tirando il filo di "perché è morto?" sei passato attraverso: sessioni utente, systemd, linger, cgroup, OOM killer, segnali, memoria virtuale, PTY, SSH keepalive.
Sono tutti pezzi dello stesso puzzle. Linux non è una collezione di comandi da memorizzare — è un sistema dove ogni componente ha un ruolo, una ragione, e una relazione con gli altri. Il terminale è un PTY che passa per una line discipline. I processi nascono per clonazione e muoiono per segnale. La memoria è un gioco di prestigio del kernel. SSH è un tunnel cifrato che crea sessioni, e quelle sessioni tengono vivo il tuo user manager.
Non devi sapere tutto a memoria. Ma sapere che esiste è la differenza tra chi usa Linux e chi lo capisce. La prossima volta che qualcosa si rompe — e si romperà — saprai dove guardare. Non su Stack Overflow: nei log, nei cgroup, nei segnali, nel sistema.
E tmux non crasherà più. Perché adesso sai perché lo faceva, e l'hai risolto con una riga.
loginctl enable-linger giobi
Una riga che prima non avresti nemmeno capito. Adesso sì.