Salta al contenuto principale

lscpu: conoscere il processore della macchina

Inviato da tuxsa il

Prima di installare un servizio, dimensionare un server o confrontare due macchine, una domanda ricorre sempre: che processore ha, e quanti core? Si potrebbe leggere il file /proc/cpuinfo, ma è lungo e ripetitivo: una sezione per ogni CPU. lscpu fa lo stesso lavoro e lo presenta in modo leggibile, in poche righe.

Cos'è

lscpu (list CPU) fa parte del pacchetto util-linux, lo stesso di lsblk e mount. Non misura niente e non ha bisogno di privilegi: raccoglie le informazioni che il kernel espone su /proc/cpuinfo e in /sys/devices/system/cpu/ (vedi l'articolo su uname per cosa è il kernel) e le riordina.

Cosa fa

Mostra l'architettura, il modello, il numero di CPU, di core e di socket (gli zoccoli fisici della scheda madre), le dimensioni delle cache, le caratteristiche supportate (le flags) e lo stato delle protezioni contro le vulnerabilità dei processori. Può anche produrre tabelle per singola CPU e dati in formato JSON o CSV, adatti agli script.

Come si usa

$ lscpu [opzioni]

Gli esempi indicati come "sandbox" sono output reali di una macchina virtuale di prova (2 CPU, un solo socket); gli altri, con web01, il server degli articoli precedenti, sono illustrativi.

Esempio 1: il riepilogo

$ lscpu
Architecture:                            x86_64
CPU op-mode(s):                          32-bit, 64-bit
Address sizes:                           52 bits physical, 57 bits virtual
Byte Order:                              Little Endian
CPU(s):                                  2
On-line CPU(s) list:                     0,1
Vendor ID:                               GenuineIntel
Model name:                              Intel(R) Xeon(R) Processor @ 2.10GHz
CPU family:                              6
Model:                                   207
Thread(s) per core:                      1
Core(s) per socket:                      2
Socket(s):                               1
...

L'output (sandbox, abbreviato) è un elenco campo: valore. Le righe da leggere per prime: Architecture (x86_64 sui PC e sui server comuni; aarch64 sulle architetture ARM, come i Raspberry Pi recenti), CPU(s) (quante CPU vede il sistema), Model name (il nome commerciale) e le tre righe su thread, core e socket (esempio 2). Su un sistema in italiano i nomi dei campi compaiono tradotti: la struttura è la stessa, ma non conviene cercarli per nome in uno script (vedi "Attenzione a"). Più sotto l'elenco prosegue con le cache, i nodi NUMA e le vulnerabilità, e le Flags, una riga lunghissima.

Esempio 2: core, thread e socket

$ lscpu | grep -E '^(CPU\(s\)|Thread|Core|Socket)'
CPU(s):                                  2
Thread(s) per core:                      1
Core(s) per socket:                      2
Socket(s):                               1
$ nproc
2
$ nproc --all
2
$ lscpu | grep -E '^(CPU\(s\)|Thread|Core|Socket)'     # esempio su web01
CPU(s):                                  8
Thread(s) per core:                      2
Core(s) per socket:                      4
Socket(s):                               1

I primi due blocchi sono del sandbox, l'ultimo è illustrativo. La regola da ricordare: CPU(s) = socket × core per socket × thread per core. Nell'esempio di web01: 1 × 4 × 2 = 8. Un thread per core uguale a 2 indica l'hyper-threading (o SMT): ogni core fisico si presenta al sistema come due CPU logiche, che però non rendono come due core veri. Il comando nproc (parte di coreutils) stampa solo il numero di CPU disponibili al processo; con --all le conta tutte, anche quelle che il sistema non può usare in quel momento (per esempio quelle limitate da un container). Un dettaglio utile per programmi come make -j o xargs -P (vedi l'articolo su xargs), che chiedono quanti processi eseguire in parallelo: -j$(nproc).

Esempio 3: una riga per ogni CPU

$ lscpu -e
CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE
  0    0      0    0 0:0:0:0          yes
  1    0      0    1 1:1:1:0          yes
$ lscpu -e=CPU,CORE,SOCKET,ONLINE
CPU CORE SOCKET ONLINE
  0    0      0    yes
  1    1      0    yes
$ lscpu -e     # esempio su web01 (4 core, 8 CPU)
CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE
  0    0      0    0 0:0:0:0          yes
  1    0      0    1 1:1:1:0          yes
  2    0      0    2 2:2:2:0          yes
  3    0      0    3 3:3:3:0          yes
  4    0      0    0 0:0:0:0          yes
  5    0      0    1 1:1:1:0          yes
  6    0      0    2 2:2:2:0          yes
  7    0      0    3 3:3:3:0          yes

Con -e (extended) si ha una tabella con una riga per CPU. Con -e=COLONNE si scelgono le colonne (l'elenco completo si vede con lscpu --help). Il blocco di web01 è illustrativo, e mostra come riconoscere le CPU "gemelle" di un hyper-threading: quelle con lo stesso numero di CORE (qui 0 e 4) sono lo stesso core fisico. La numerazione delle CPU gemelle cambia da macchina a macchina: in alcune sono vicine (0 e 1), in altre distanti come qui. La colonna ONLINE dice se la CPU è in uso. Con -p (parse) lo stesso elenco è in formato CSV, senza intestazione (le righe che iniziano con # sono commenti).

Esempio 4: le caratteristiche del processore (flags)

$ lscpu | grep -o -w -E 'avx2|aes|vmx|svm|lm|hypervisor' | sort -u
aes
avx2
hypervisor
lm
$ lscpu | grep -c -w avx2
1
$ grep -c -E 'vmx|svm' /proc/cpuinfo
0

La riga Flags elenca le istruzioni e le funzioni che il processore sa eseguire. Con grep -o -w (-o stampa solo la parte trovata, -w cerca la parola intera: vedi l'articolo su grep) si controlla se ne ha qualcuna in particolare. Le più utili: lm (long mode, cioè un processore a 64 bit), aes (cifratura AES accelerata, importante per VPN e dischi cifrati), avx2 (istruzioni vettoriali, richieste da certi programmi), vmx (Intel) o svm (AMD) per la virtualizzazione: senza di queste, KVM non funziona. Nel sandbox vmx non compare (il conteggio è 0) perché è già una macchina virtuale, e infatti c'è hypervisor. Su una macchina fisica con la virtualizzazione attiva nel BIOS, il comando lscpu | grep -i virtualization mostra una riga Virtualization: VT-x (Intel) o AMD-V.

Esempio 5: le cache

$ lscpu | grep -E 'cache|NUMA node\('
L1d cache:                               96 KiB (2 instances)
L1i cache:                               64 KiB (2 instances)
L2 cache:                                4 MiB (2 instances)
L3 cache:                                260 MiB (1 instance)
NUMA node(s):                            1
$ lscpu -C
NAME ONE-SIZE ALL-SIZE WAYS TYPE        LEVEL   SETS PHY-LINE COHERENCY-SIZE
L1d       48K      96K   12 Data            1     64        1             64
L1i       32K      64K    8 Instruction     1     64        1             64
L2         2M       4M   16 Unified         2   2048        1             64
L3       260M     260M   20 Unified         3 212992        1             64

Le cache sono piccole memorie velocissime dentro il processore: L1 (divisa in dati, L1d, e istruzioni, L1i) per ogni core, L2 più grande, L3 condivisa. Il riepilogo mostra la somma e il numero di "istanze" (una per core, per la L1 e la L2); -C (caches) dà una tabella con la dimensione di una singola cache (ONE-SIZE) e il totale. Le dimensioni dell'L3 di una macchina virtuale possono essere inattendibili: quelle del sandbox (260 MiB) sono quelle dell'host fisico, non una risorsa riservata alla macchina. Con -B (bytes) le dimensioni escono in byte esatti, comodo negli script. Il numero dei NUMA node è 1 nella maggior parte dei server piccoli; è maggiore di 1 sui server con più socket, dove la memoria è divisa tra i processori.

Esempio 6: le vulnerabilità e le protezioni

$ lscpu | grep -E 'Vulnerability (Meltdown|L1tf|Mds|Retbleed)'
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Retbleed:                  Not affected
$ cat /sys/devices/system/cpu/vulnerabilities/meltdown
Not affected
$ lscpu | grep -c '^Vulnerability'
19

Negli ultimi anni sono stati scoperti molti difetti dei processori (Meltdown, Spectre e successivi) che permettono di leggere dati che non dovrebbero essere accessibili. Il kernel applica delle protezioni, e lscpu riassume la situazione: Not affected (il modello non ha il problema), Mitigation; ... (il problema c'è, ma il kernel lo contiene) o Vulnerable (nessuna protezione attiva: è il caso da approfondire). Le informazioni vengono dai file di /sys/devices/system/cpu/vulnerabilities/, uno per vulnerabilità, che si possono leggere anche con cat. Il numero di righe (19 nel sandbox) dipende dal kernel e dalla versione di lscpu. Ricorda che le protezioni possono costare un po' di prestazioni.

Esempio 7: JSON e script

$ lscpu -J | jq -r '.lscpu[] | select(.field=="CPU(s):") | .data'
2
$ lscpu -J | jq -r '.lscpu[] | select(.field=="Model name:") | .data'
Intel(R) Xeon(R) Processor @ 2.10GHz
$ lscpu | awk -F': +' '/^Model name/ {print $2}'
Intel(R) Xeon(R) Processor @ 2.10GHz
$ lscpu -p=CPU,CORE,SOCKET | grep -v '^#' | cut -d, -f3 | sort -u | wc -l
1

Per usare i dati in uno script, senza interpretare il testo a occhio, -J (JSON) produce una struttura che jq sa interrogare: è un elenco di coppie field / data, e i nomi dei campi finiscono con i due punti ("CPU(s):"). Oppure si usa awk (vedi l'articolo relativo) con i due punti come separatore. L'ultimo esempio conta i socket: con -p si ottiene l'elenco in CSV, si toglie la riga di commento, si prende la terza colonna e si contano i valori diversi. Tutti questi comandi sono stati provati nel sandbox. jq non è sempre installato (sudo apt install jq: vedi l'articolo su apt).

Esempio 8: uso negli script

#!/bin/bash
# Controlla che la macchina abbia i requisiti minimi per un servizio
MIN_CPU=2
CPU=$(nproc --all)
if [ "$CPU" -lt "$MIN_CPU" ]; then
    echo "CPU insufficienti: $CPU, servono almeno $MIN_CPU" >&2
    exit 1
fi
for FLAG in avx2 aes; do
    if ! lscpu | grep -q -w "$FLAG"; then
        echo "Manca la caratteristica $FLAG" >&2
        exit 1
    fi
done
echo "OK: $CPU CPU, $(lscpu | awk -F': +' '/^Model name/{print $2}')"

Lo script controlla il numero di CPU e la presenza di due caratteristiche, e si ferma con un errore se qualcosa manca (exit 1, così un altro script o cron può accorgersene: vedi l'articolo su crontab). L'ho provato nel sandbox: con i valori minimi originali risponde OK: 2 CPU, Intel(R) Xeon(R) Processor @ 2.10GHz; alzando il minimo a 8 si ferma con "CPU insufficienti", e cercando una caratteristica inesistente con "Manca la caratteristica". Il confronto con grep -q -w è sulle flags, che si chiamano allo stesso modo in tutte le lingue.

Opzioni utili

  • lscpu: riepilogo; -e[=colonne]: tabella per singola CPU; -p[=colonne]: CSV; -J: JSON.
  • -C: tabella delle cache; -B: dimensioni in byte; -x: maschere in esadecimale.
  • -a: anche le CPU spente; -b: solo quelle accese; -c: solo quelle spente.
  • -y: numeri fisici invece di quelli logici; --hierarchic=always: righe annidate (le informazioni dipendenti sono rientrate).
  • --output-all: tutte le colonne disponibili per -e, -p o -C.
  • nproc, nproc --all: solo il numero di CPU; /proc/cpuinfo: i dati grezzi.
  • uname -m, top, uptime, free: architettura e carico di lavoro (articoli precedenti); lsblk: i dischi.

Attenzione a

  • CPU non vuol dire core: con l'hyper-threading, 8 CPU sono 4 core fisici. Per pianificare le prestazioni, guarda Core(s) per socket e Socket(s), non solo CPU(s).
  • Nelle macchine virtuali i dati sono quelli che l'ambiente vuole mostrare: il modello può essere generico, le cache quelle dell'host e le frequenze assenti (nel sandbox le colonne MAXMHZ e MINMHZ sono vuote, con un trattino). Il numero di CPU è quello assegnato alla macchina, non quello dell'host.
  • Nei container le CPU "viste" non sono quelle "utilizzabili": lscpu mostra le CPU della macchina ospite, anche se il container è limitato a una parte (con i limiti di Docker o di cgroup). Per il numero effettivo di CPU usabili, nproc è più affidabile, anche se non sempre tiene conto del limite di tempo di CPU.
  • Le frequenze cambiano: il valore nel nome del modello (@ 2.10GHz) è quello nominale. La frequenza attuale (colonna MHZ) varia secondo il carico, il risparmio energetico e la temperatura: per seguirla nel tempo si può usare watch (vedi l'articolo relativo) con lscpu -e=CPU,MHZ.
  • Le etichette dipendono dalla lingua: su un sistema in italiano i campi di lscpu sono tradotti e un filtro come grep '^Model name' non trova niente. Negli script e nelle guide condivise, anteponi LC_ALL=C (LC_ALL=C lscpu | grep '^Model name'), oppure usa -J / -p, dove i campi sono stabili. Per le righe di esempio ho riportato i nomi inglesi: nel sandbox non c'era una lingua italiana da provare, quindi non ho verificato le traduzioni esatte.
  • x86_64 non basta a dire "compatibile": programmi compilati per istruzioni recenti (come avx2 o avx512) non partono su processori che non le hanno, anche se l'architettura è la stessa. Un errore come "Illegal instruction" è il sintomo: controlla le flags.
  • Vulnerable non va ignorato, ma neanche preso come un allarme da solo: dipende dal modello del processore, dal kernel e dal microcodice. Si risolve di solito aggiornando il kernel e il microcodice (apt), non con lscpu.
  • Non misura il carico: dice com'è fatta la CPU, non quanto è occupata. Per questo si usano top e uptime.
  • Un solo output non descrive tutti i sistemi: i campi variano tra Intel, AMD e ARM (per esempio, su ARM compaiono Model name e Features invece di Flags), e anche tra versioni di lscpu.

Riepilogo

ComandoCosa fa
lscpuRiepilogo del processore
lscpu | grep -E '^(CPU\(s\)|Thread|Core|Socket)'CPU, thread, core e socket
nproc, nproc --allNumero di CPU utilizzabili, o di tutte
lscpu -eTabella con una riga per CPU
lscpu -e=CPU,CORE,SOCKETTabella con le colonne scelte
lscpu | grep -o -w -E 'avx2|aes'Controlla alcune caratteristiche
lscpu -CTabella delle cache
lscpu | grep '^Vulnerability'Stato delle protezioni del processore
lscpu -JOutput in JSON (con jq)
lscpu -pOutput in CSV, per gli script
LC_ALL=C lscpuOutput con le etichette in inglese