27.2. /proc

La directory /proc, in realtà, è uno pseudo-filesystem. I file in essa contenuti rispecchiano il sistema correntemente in esecuzione, i processi del kernel, ed informazioni e statistiche su di essi.

bash$ cat /proc/devices
Character devices:
   1 mem
   2 pty
   3 ttyp
   4 ttyS
   5 cua
   7 vcs
  10 misc
  14 sound
  29 fb
  36 netlink
 128 ptm
 136 pts
 162 raw
 254 pcmcia

 Block devices:
   1 ramdisk
   2 fd
   3 ide0
   9 md



bash$ cat /proc/interrupts
           CPU0       
   0:      84505          XT-PIC  timer
   1:       3375          XT-PIC  keyboard
   2:          0          XT-PIC  cascade
   5:          1          XT-PIC  soundblaster
   8:          1          XT-PIC  rtc
  12:       4231          XT-PIC  PS/2 Mouse
  14:     109373          XT-PIC  ide0
 NMI:          0 
 ERR:          0


bash$ cat /proc/partitions
major minor  #blocks  name     rio rmerge rsect ruse wio wmerge wsect wuse running use aveq

    3     0    3007872 hda 4472 22260 114520 94240 3551 18703 50384 549710 0 111550 644030
    3     1      52416 hda1 27 395 844 960 4 2 14 180 0 800 1140
    3     2          1 hda2 0 0 0 0 0 0 0 0 0 0 0
    3     4     165280 hda4 10 0 20 210 0 0 0 0 0 210 210
    ...



bash$ cat /proc/loadavg
0.13 0.42 0.27 2/44 1119



bash$ cat /proc/apm
1.16 1.2 0x03 0x01 0xff 0x80 -1% -1 ?



bash$ cat /proc/acpi/battery/BAT0/info
present:                 yes
 design capacity:         43200 mWh
 last full capacity:      36640 mWh
 battery technology:      rechargeable
 design voltage:          10800 mV
 design capacity warning: 1832 mWh
 design capacity low:     200 mWh
 capacity granularity 1:  1 mWh
 capacity granularity 2:  1 mWh
 model number:            IBM-02K6897
 serial number:            1133
 battery type:            LION
 OEM info:                Panasonic
	
	
	
bash$ fgrep Mem /proc/meminfo
MemTotal:       515216 kB
 MemFree:        266248 kB
         

Gli script di shell possono ricavare dati da alcuni dei file presenti in /proc. [1]

FS=iso                       #  Il supporto per il filesystem ISO è
                             #+ abilitato nel kernel?
grep $FS /proc/filesystems   # iso9660

versione_kernel=$( awk '{ print $3 }' /proc/version )

CPU=$( awk '/model name/ {print $4}' < /proc/cpuinfo )

if [ $CPU = Pentium ]
then
  esegui_dei_comandi
  ...
else
  esegui_altri_comandi
  ...
fi


velocita_cpu=$( fgrep "cpu MHz" /proc/cpuinfo | awk '{print $4}' )
#  Velocità operativa corrente (in MHz) della cpu della vostra macchina.
#  Su un portatile potrebbe variare, in dipendenza dell'uso della batteria
#+ o dell'alimentazione AC.

+

filedisp="/proc/bus/usb/devices"
USB1="Spd=12"
USB2="Spd=480"


veloc_bus=$(grep Spd $filedisp | awk '{print $9}')

if [ "$veloc_bus" = "$USB1" ]
then
  echo "Trovata porta USB 1.1."
  # Comandi inerenti alla porta USB 1.1.
fi

Nota

È anche possibile controllare certe periferiche tramite dei comandi inviati alla directory /proc.

	root# echo on > /proc/acpi/ibm/light
	
Questo accende la Thinklight in certi modelli di portatili IBM/Lenovo.

Naturalmente, occorre prestare molta attenzione quando si scrive in /proc.

La directory /proc contiene delle sottodirectory con strani nomi numerici. Ognuno di questi nomi traccia l'ID di processo del processo correntemente in esecuzione. All'interno di ognuna di queste sottodirectory, vi è un certo numero di file contenenti utili informazioni sui processi corrispondenti. I file stat e status contengono statistiche continuamente aggiornate del processo, il file cmdline gli argomenti da riga di comando con i quali il processo è stato invocato e il file exe è un link simbolico al percorso completo del processo chiamante. Di tali file ve ne sono anche altri (pochi), ma quelli elencati sembrano essere i più interessanti dal punto di vista dello scripting.

Esempio 27-2. Trovare il processo associato al PID

#!/bin/bash
# pid-identifier.sh: 
# Fornisce il percorso completo del processo associato al pid.

ARGNUM=1  # Numero di argomenti attesi dallo script.
E_ERR_ARG=65
E_ERR_PID=66
E_ERR_PROCESSO=67
E_ERR_PERMESSO=68
FILEPROC=exe

if [ $# -ne $ARGNUM ]
then
  echo "Utilizzo: `basename $0` numero PID" >&2  # Messaggio d'errore >stderr.
  exit $E_ERR_ARG
fi

pidnum=$( ps ax | grep $1 | awk '{ print $1 }' | grep $1 )
#  Controlla il pid nell'elenco di "ps", campo nr.1.
#  Quindi si accerta che sia il processo effettivo, non quello invocato dallo
#+ script stesso.
#  L'ultimo "grep $1" scarta questa possibilità.
#
#    Come ha evidenziato Teemu Huovila, funziona anche:
#    numpid=$( ps ax | awk '{ print $1 }' | grep $1 )

if [ -z "$pidnum" ]  #  Se, anche dopo il filtraggio, il risultato è una
                     #+ stringa di lunghezza zero, 
then                 #  significa che nessun processo in esecuzione
                     #+ corrisponde al pid dato.
  echo "Il processo non è in esecuzione."
  exit $E_ERR_PROCESSO
fi

# In alternativa:
#   if ! ps $1 > /dev/null 2>&1
#   then             # nessun processo in esecuzione corrisponde al pid dato.
#     echo "Il processo non è in esecuzione."
#     exit $E_ERR_PROCESSO
#    fi

# Per semplificare l'intera procedura, si usa "pidof".


if [ ! -r "/proc/$1/$FILEPROC" ]  # Controlla i permessi in lettura.
then
  echo "Il processo $1 è in esecuzione, ma..."
  echo "Non ho il permesso di lettura su /proc/$1/$FILEPROC."
  exit $E_ERR_PERMESSO  #  Un utente ordinario non può accedere ad alcuni
                        #+ file di /proc.
fi

# Le due ultime verifiche possono essere sostituite da:
#    if ! kill -0 $1 > /dev/null 2>&1 # '0' non è un segnale, ma
                                      # verifica la possibilità
                                      # di inviare un segnale al processo.
#    then echo "Il PID non esiste o non sei il suo proprietario" >&2
#    exit $E_ERR_PID
#    fi


file_exe=$( ls -l /proc/$1 | grep "exe" | awk '{ print $11 }' )
#  Oppure   file_exe=$( ls -l /proc/$1/exe | awk '{print $11}' )
#
#  /proc/numero-pid/exe è un link simbolico
#+ al nome completo del processo chiamante.

if [ -e "$file_exe" ]  #  Se /proc/numero-pid/exe esiste,
then                   #+ esiste anche il corrispondente processo.
  echo "Il processo nr.$1 è stato invocato da $file_exe."
else
  echo "Il processo non è in esecuzione."
fi  


#  Questo elaborato script si potrebbe *quasi* sostituire con
#       ps ax | grep $1 | awk '{ print $5 }'
#  Questa forma, però, non funzionerebbe...
#+ perché il quinto campo di 'ps' è  l'argv[0] del processo,
#+ non il percorso del file eseguibile.
#
# Comunque, entrambi i seguenti avrebbero funzionato.
#       find /proc/$1/exe -printf '%l\n'
#       lsof -aFn -p $1 -d txt | sed -ne 's/^n//p'

# Commenti aggiuntivi di Stephane Chazelas.

exit 0

Esempio 27-3. Stato di una connessione

#!/bin/bash

NOMEPROC=pppd        # Demone ppp
NOMEFILEPROC=status  # Dove guardare.
NONCONNESSO=65
INTERVALLO=2         # Aggiorna ogni 2 secondi.

pidnum=$( ps ax | grep -v "ps ax" | grep -v grep | grep $NOMEPROC \
| awk '{ print $1 }' )
                                  
# Ricerca del numero del processo di 'pppd', il 'demone ppp'.
# Occorre eliminare le righe del processo generato dalla ricerca stessa.
#
#  Comunque, come ha evidenziato Oleg Philon,
#+ lo si sarebbe potuto semplificare considerevolmente usando "pidof".
#  pidnum=$( pidof $NOMEPROC )
#
#  Morale della favola:
#  Quando una sequenza di comandi diventa troppo complessa, cercate una
#+ scorciatoia.


if [ -z "$pidnum" ]   #  Se non c'è il pid, allora il processo non è
                      #+ in esecuzione.
then
  echo "Non connesso."
  exit $NONCONNESSO
else
  echo "Connesso."; echo
fi

while [ true ]       # Ciclo infinito. Qui lo script può essere migliorato.
do

  if [ ! -e "/proc/$pidnum/$NOMEFILEPROC" ]
  # Finché il processo è in esecuzione, esiste il file "status".
  then
      echo "Disconnesso."
      exit $NONCONNESSO
  fi

netstat -s | grep "packets received"  # Per avere alcune statistiche.
netstat -s | grep "packets delivered"


  sleep $INTERVALLO
  echo; echo

done

exit 0

# Così com'è, lo script deve essere terminato con Control-C.

#    Esercizi:
#    ---------
#    Migliorate lo script in modo che termini alla pressione del tasto "q".
#    Rendete lo script più amichevole inserendo altre funzionalità

Avvertimento

In generale, è pericoloso scrivere nei file presenti in /proc perché questo potrebbe portare alla corruzione del filesystem o al crash della macchina.

Note

[1]

Alcuni comandi di sistema, come procinfo, free, vmstat, lsdev, e uptime svolgono lo stesso compito.