← Fundamentos de Sistemas Operativos Bloque 7 · Visión general de los servicios · Tema 31 de 34

Tema 31

Una ventana al kernel: /proc y /sys

En casi todos los temas leímos algún archivo de /proc o de /sys para ver qué hacía el kernel, sin detenernos en qué eran. Llegó el momento de mirarlos de frente: cómo es posible leer un archivo que no está en ningún disco, qué hay en cada rincón de esos dos directorios y cómo usarlos desde un script o desde un programa en C.

1. Preguntarle al kernel

El kernel sabe cosas que ningún programa puede averiguar por su cuenta: qué procesos existen, cuánta memoria queda, cuántas interrupciones llegaron, qué dispositivos hay conectados. Esa información está en sus tablas, en el espacio de kernel, donde los programas no pueden leer. Para obtenerla hay que pedírsela, y existen dos maneras de organizar ese pedido.

Una llamada al sistema por cada datoArchivos
El programaLlama a una función específica y recibe una estructuraAbre un archivo y lee texto
Ejemplos que usamosuname, getpid, getrusage, sysconf/proc/meminfo, /proc/interrupts
Añadir un dato nuevoRequiere una llamada nueva, y un envoltorio en la bibliotecaRequiere un archivo nuevo, o una línea más
Se puede usar desdeUn lenguaje que tenga acceso a esa llamadaCualquier lenguaje, y la shell
CostoMínimoDar formato al texto y volver a interpretarlo

Linux usa las dos, y para casi todo lo que no es urgente eligió la segunda. Es la idea de «todo es un archivo» del tema 28, llevada un paso más allá: no solo los dispositivos, también el estado interno del kernel se presenta como archivos. La ventaja la aprovechamos durante todo el curso sin notarlo: para observar el sistema bastaron cat, grep y head.

Hay dos directorios de este tipo, con propósitos distintos:

/proc/sys
OrigenAños ochenta; en Linux, desde el principio2003
Contenido principalLos procesos, y estadísticas generales del sistemaLos dispositivos, los drivers y la configuración del kernel
FormatoVariado: cada archivo, el suyoUniforme: un valor por archivo
OrganizaciónCreció por acumulaciónUn árbol que refleja la estructura de los dispositivos

2. Archivos que se escriben al leerlos

En el tema 28 vimos que /proc y /sys son sistemas de archivos sin disco detrás, y que sus archivos declaran un tamaño de cero bytes. El contenido no está guardado en ningún lado: se produce en el momento de la lectura.

terminal
$ ls -l /proc/uptime
-r--r--r-- 1 root root 0 oct 12 14:30 /proc/uptime
$ cat /proc/uptime; sleep 2; cat /proc/uptime
18961.42 149812.07
18963.43 149827.91
$ strace -e trace=openat,read cat /proc/uptime 2>&1 | tail -5
openat(AT_FDCWD, "/proc/uptime", O_RDONLY) = 3
read(3, "18965.47 149843.80\n", 131072) = 19
18965.47 149843.80
read(3, "", 131072)                     = 0
+++ exited with 0 +++

Dos lecturas separadas por dos segundos dan contenidos distintos, sin que nadie haya escrito en el archivo. La traza muestra que cat no hace nada especial: abre y lee, como con cualquier archivo. La diferencia está del otro lado de la llamada.

Recorrido de una lectura de proc: el programa llama a read; el sistema de archivos virtual dirige la llamada a una función del kernel propia de ese archivo, que consulta las tablas del kernel y devuelve el resultado como texto Programa cat, ps, top llama a read VFS ¿de quién es este archivo? Función propia de ese archivo Tablas procesos, memoria, tiempo
La función resaltada consulta los datos actuales y los convierte en texto. El programa recibe ese texto como contenido del archivo.

Es el mecanismo del tema 29. Allí, el sistema de archivos virtual dirigía la llamada read a la función de un driver; aquí la dirige a una función que, en lugar de consultar un dispositivo, consulta una variable del kernel. Esta es la de /proc/uptime, reducida a lo esencial:

del código fuente de Linux, fs/proc/uptime.c (simplificado)
static int uptime_proc_show(struct seq_file *salida, void *v)
{
    /* … obtener el tiempo desde el arranque y el tiempo ocioso acumulado … */

    seq_printf(salida, "%lu.%02lu %lu.%02lu\n",
               segundos, centesimas, ocioso_segundos, ocioso_centesimas);
    return 0;
}

Un printf dentro del kernel, cuyo resultado va a parar al búfer del programa que lee. Cada archivo de /proc tiene detrás una función como esta. Leer /proc/meminfo ejecuta una que recorre los contadores de memoria; leer /proc/interrupts, otra que recorre los de interrupciones. De ahí tres propiedades que conviene recordar:

3. El mapa de /proc

El directorio tiene dos clases de entradas, que se distinguen a simple vista:

terminal (salida abreviada)
$ ls /proc
1     2     3     388   905   1042  2011  2204  4102  …
cmdline   cpuinfo  devices     diskstats  filesystems  interrupts  iomem
ioports   kallsyms loadavg     meminfo    modules      mounts      partitions
self      softirqs stat        sys        uptime       version     vmstat

Los nombres numéricos son un directorio por cada proceso; los vemos en la sección siguiente. Los demás describen el sistema en su conjunto. Casi todos aparecieron a lo largo del curso, y esta tabla los reúne por tema:

ArchivoContenidoLo usamos en
version, cmdlineLa versión del kernel y los parámetros con que arrancó.Temas 11 y 21
uptime, loadavgEl tiempo desde el arranque y la carga media.Temas 11 y 26
cpuinfoLa descripción de cada procesador.Tema 2
statContadores globales: tiempo de CPU por modo, interrupciones, cambios de contexto.Temas 12, 17 y 19
meminfoEl estado de la memoria.Temas 5, 7 y 27
interrupts, softirqsLos contadores de interrupciones, por origen y por procesador.Tema 17
ioports, iomemEl mapa de puertos y de memoria de los dispositivos.Tema 8
devicesLos drivers registrados, con su número mayor.Tema 29
modulesLos módulos cargados. Es lo que muestra lsmod.Tema 24
kallsymsLos símbolos del kernel, con sus direcciones.Tema 11
mounts, filesystemsLos sistemas de archivos montados, y los tipos que el kernel conoce.Tema 28
partitions, diskstatsLos dispositivos de bloques y sus estadísticas de actividad.—
sys/Parámetros del kernel que se pueden leer y modificar.Sección 8

La documentación está en el manual: man 5 proc describe cada archivo y cada campo. Es la referencia a consultar antes de usar uno en un programa.

4. Un directorio por proceso

En el tema 26 presentamos /proc/PID como la tabla de componentes de un proceso hecha visible. La lista de entonces se puede completar:

EntradaContenidoLo usamos en
statusLos datos principales, en formato legible: nombre, estado, identificadores, memoria, cambios de contexto, capacidades.Temas 11, 19, 27 y 30
statLos mismos datos y más, en una sola línea de números. Es lo que leen ps y top.—
comm, cmdlineEl nombre del programa, y los argumentos con que se lanzó.Tema 26
exe, cwd, rootEnlaces al ejecutable, al directorio actual y al directorio raíz del proceso.Tema 26
environSus variables de entorno.—
maps, smaps_rollupEl mapa de memoria, y el resumen de cuánta ocupa.Temas 9 y 27
fd/Un enlace por cada descriptor abierto.Tema 28
task/Un subdirectorio por cada hilo.Tema 26
limitsLos límites de recursos: tamaño de pila, archivos abiertos, memoria.Tema 27, como ulimit
ioCuántos bytes leyó y escribió.—

Hay una entrada que resuelve un problema práctico. Un programa que quiere consultar sus propios datos tendría que averiguar su PID y armar la ruta. Para evitarlo existe /proc/self, un enlace simbólico que apunta al directorio del proceso que lo está leyendo:

terminal
$ readlink /proc/self; readlink /proc/self
9212
9213
$ cat /proc/$$/stat
4102 (bash) S 3310 4102 4102 34816 9214 4194304 2811 14306 0 3 14 6 21 9 20 0 1 0 418230 11694080 1408 …

El enlace devuelve un número distinto cada vez, porque cada readlink es un proceso nuevo, y se ve a sí mismo. Es el archivo que usamos como /proc/self/maps y /proc/self/status en varios programas.

El segundo comando muestra el archivo stat de la shell. Son 52 números en una línea, pensados para programas y no para personas. Los primeros se reconocen: el PID, el nombre entre paréntesis, el estado y el PID del padre. Los demás incluyen los tiempos de CPU, la prioridad, la cantidad de hilos y los tamaños de memoria. Es el formato compacto del que ps y top sacan sus columnas.

No todas las entradas son legibles para cualquiera. Las que revelan información delicada —el entorno, los descriptores, el mapa de memoria— solo puede leerlas el dueño del proceso o el administrador. Son archivos, y se protegen con los permisos del tema 30.

5. Experimento: un ps en veinte líneas

Si todo lo que ps muestra está en archivos, se lo puede escribir sin ninguna llamada especial. El siguiente script recorre los directorios numéricos de /proc y extrae de cada uno cuatro datos:

procesos.sh
#!/bin/bash
# Un ps mínimo: recorre /proc y muestra PID, PPID, estado y nombre.

printf '%7s %7s %-6s %s\n' PID PPID ESTADO NOMBRE

for dir in /proc/[0-9]*; do
    pid=${dir#/proc/}

    # Un proceso puede terminar mientras lo leemos: si falla, pasar al siguiente.
    nombre=$(cat "$dir/comm" 2>/dev/null) || continue
    ppid=$(awk '/^PPid:/ {print $2}' "$dir/status" 2>/dev/null)
    estado=$(awk '/^State:/ {print $2}' "$dir/status" 2>/dev/null)

    printf '%7s %7s %-6s %s\n' "$pid" "$ppid" "$estado" "$nombre"
done
terminal
$ chmod +x procesos.sh
$ ./procesos.sh | sort -n | head -6
    PID    PPID ESTADO NOMBRE
      1       0 S      systemd
      2       0 S      kthreadd
      3       2 S      pool_workqueue_release
      4       2 I      kworker/R-rcu_g
      5       2 I      kworker/R-rcu_p
$ ./procesos.sh | tail -n +2 | wc -l
287

El resultado coincide con el de ps -eo pid,ppid,stat,comm. El script confirma varias cosas vistas en el tema 26: los procesos 1 y 2 tienen padre 0, y todos los hilos del kernel descienden del 2. Y deja ver un detalle del tema 11: los nombres aparecen cortados a quince caracteres, porque el campo comm de la estructura task_struct es un arreglo de dieciséis bytes.

El comentario del script señala la única dificultad real de trabajar con /proc. El sistema no se detiene mientras lo miramos. Entre el momento en que la shell obtiene la lista de directorios y el momento en que lee cada uno, algunos procesos terminan y sus directorios desaparecen. Todo programa que recorre /proc debe tolerar que un archivo que estaba ya no esté.

El script es también mucho más lento que ps, porque lanza tres procesos por cada proceso que examina. El ps real hace lo mismo que él, en C: abre cada directorio y lee los archivos stat y status.

6. La CPU: cpuinfo y stat

Dos archivos describen los procesadores. /proc/cpuinfo dice qué son: tiene un bloque de líneas por cada procesador, con el modelo, la frecuencia y la lista de instrucciones opcionales que admite. /proc/stat dice qué hicieron.

terminal
$ grep -c '^processor' /proc/cpuinfo
8
$ head -1 /proc/stat
cpu  310452 1204 118730 14709201 20311 0 8102 0 0 0

La primera línea de /proc/stat acumula, desde el arranque y para todos los procesadores juntos, el tiempo pasado en cada situación. Son los campos de la tercera línea de top que explicamos en el tema 12, en su forma original:

PosiciónValorTiempo…En top
1310452ejecutando programas, en modo usuarious
21204ejecutando programas de baja prioridadni
3118730en modo kernel, atendiendo llamadas al sistemasy
414709201sin nada que hacerid
520311sin nada que hacer, con operaciones de disco pendienteswa
6 y 70 y 8102atendiendo interrupcioneshi, si
80cedido por el hipervisor a otras máquinas virtualesst

La unidad es la centésima de segundo, el valor que devolvía sysconf(_SC_CLK_TCK) en el tema 15. Y los valores provienen del muestreo que hace el kernel en cada tic del temporizador, según vimos en el tema 19.

Los contadores solo crecen. Un porcentaje de uso, como el que muestra top, no está en ningún archivo: se calcula leyendo dos veces y restando. El siguiente programa lo hace:

usocpu.c
#include <stdio.h>
#include <unistd.h>

/* Lee la primera línea de /proc/stat: los tiempos acumulados de todas las CPU. */
static int leer_tiempos(unsigned long long *ocupado, unsigned long long *total)
{
    unsigned long long usuario, cortesia, sistema, ocioso, espera, irq, softirq, robado;
    FILE *f = fopen("/proc/stat", "r");

    if (f == NULL)
        return -1;

    if (fscanf(f, "cpu %llu %llu %llu %llu %llu %llu %llu %llu",
               &usuario, &cortesia, &sistema, &ocioso,
               &espera, &irq, &softirq, &robado) != 8) {
        fclose(f);
        return -1;
    }
    fclose(f);

    *total = usuario + cortesia + sistema + ocioso + espera + irq + softirq + robado;
    *ocupado = *total - ocioso - espera;
    return 0;
}

int main(void)
{
    unsigned long long ocupado1, total1, ocupado2, total2;

    if (leer_tiempos(&ocupado1, &total1) == -1) {
        fprintf(stderr, "No se pudo leer /proc/stat\n");
        return 1;
    }

    for (int i = 0; i < 6; i++) {
        sleep(1);
        if (leer_tiempos(&ocupado2, &total2) == -1)
            return 1;

        /* lo ocurrido en el último segundo: la diferencia entre dos lecturas */
        printf("Uso de CPU: %5.1f %%\n",
               100.0 * (double)(ocupado2 - ocupado1) / (double)(total2 - total1));

        ocupado1 = ocupado2;
        total1 = total2;
    }
    return 0;
}
terminal
$ gcc -Wall -Wextra -g usocpu.c -o usocpu
$ ./usocpu
Uso de CPU:   5.6 %
Uso de CPU:   5.2 %
Uso de CPU:  18.1 %
Uso de CPU:  17.8 %
Uso de CPU:   5.4 %
Uso de CPU:   5.1 %

Las dos líneas resaltadas corresponden al momento en que, en otra terminal, se ejecutó ./tiempos calcular, del tema 11. Un programa que ocupa por completo uno de ocho procesadores añade un octavo del total: 12,5 puntos.

El método —leer, esperar, leer, restar— es el de todas las herramientas de monitoreo. Lo usamos en interrupciones.c, en el tema 17, sobre otra línea de este mismo archivo. Debajo de la línea cpu hay una por cada procesador, cpu0, cpu1 y siguientes, con las que se obtiene el uso de cada uno por separado.

7. La memoria: meminfo

/proc/meminfo tiene unas cincuenta líneas, cada una con un nombre, un valor y la unidad. Las que más se consultan son estas:

terminal
$ grep -E '^(MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|Dirty):' /proc/meminfo
MemTotal:       16252416 kB
MemFree:         1153024 kB
MemAvailable:   10931548 kB
Buffers:          182344 kB
Cached:          9980120 kB
SwapTotal:       2097148 kB
SwapFree:        2097148 kB
Dirty:              1284 kB
LíneaSignificadoTema
MemTotalLa memoria física que administra el kernel.27
MemFreeLa que no se usa para nada.7
MemAvailableLa que podrían obtener programas nuevos: la libre más la caché recuperable.7 y 27
Buffers, CachedLa caché de disco.7
SwapTotal, SwapFreeEl área de intercambio, y cuánto queda sin usar.7
DirtyDatos modificados en memoria que aún no se escribieron en el disco.7 y 28
Committed_ASLa memoria prometida a los procesos.27

El formato, con un nombre fijo al comienzo de cada línea, se presta a extraer valores con herramientas de texto. Esta orden calcula el porcentaje de memoria disponible:

terminal
$ awk '/^MemTotal:/ {t=$2} /^MemAvailable:/ {d=$2} END {printf "Disponible: %.0f %%\n", 100*d/t}' /proc/meminfo
Disponible: 67 %

Es lo que hace free, que no tiene otra fuente de información. Conviene buscar las líneas por su nombre y no por su posición: entre una versión del kernel y otra se añaden líneas nuevas, y un programa que confía en que MemAvailable es la tercera deja de funcionar.

8. Parámetros ajustables: /proc/sys

Hasta aquí, /proc sirvió para leer. El subdirectorio /proc/sys es distinto: sus archivos contienen parámetros de funcionamiento del kernel, y muchos se pueden escribir. Modificar el archivo modifica el comportamiento del kernel en el acto, sin reiniciar ni recompilar.

terminal
$ cat /proc/sys/kernel/ostype /proc/sys/kernel/osrelease /proc/sys/kernel/hostname
Linux
6.8.0-45-generic
equipo
$ sysctl kernel.pid_max vm.swappiness
kernel.pid_max = 4194304
vm.swappiness = 60

Los tres primeros valores son los que devuelve la llamada uname, que usamos en el tema 4: el mismo dato por las dos vías de la sección 1. El comando sysctl es una forma más cómoda de acceder a estos archivos; usa puntos donde la ruta tiene barras, de modo que vm.swappiness es /proc/sys/vm/swappiness.

Varios de estos parámetros aparecieron en el curso:

ParámetroControlaTema
kernel.pid_maxEl mayor PID que se asigna.26
kernel.randomize_va_spaceLa aleatoriedad del espacio de direcciones.27
kernel.yama.ptrace_scopeQuién puede observar un proceso con ptrace.16
kernel.taintedSi se cargó código ajeno en el kernel. Solo lectura.24
vm.drop_cachesAl escribir en él, el kernel vacía la caché de disco.7
vm.overcommit_memoryLa política de sobrecompromiso de memoria.27
vm.swappinessLa inclinación del kernel a usar el intercambio.—

Modificar un parámetro requiere ser administrador, y hay dos formas equivalentes:

terminal
$ sudo sysctl -w vm.swappiness=10
vm.swappiness = 10
$ echo 60 | sudo tee /proc/sys/vm/swappiness     # la otra forma; restablece el valor
60

La segunda forma explica una construcción que usamos en el tema 7 sin comentarla. No sirve escribir sudo echo 60 > archivo, porque la redirección la hace la shell, con los permisos del usuario, antes de ejecutar sudo: es la mecánica del tema 28. Con tee, quien abre el archivo es un programa que ya corre como administrador.

Lo escrito de este modo dura hasta el próximo reinicio: son variables en la memoria del kernel. Para que un valor sea permanente se lo anota en un archivo de /etc/sysctl.d, que el sistema de inicio aplica en cada arranque.

9. /sys: un objeto por directorio

/proc creció durante años sin un plan: cada desarrollador añadía un archivo con el formato que le parecía. Cuando hizo falta un lugar para describir los dispositivos, se creó otro sistema de archivos con reglas estrictas, /sys:

En el tema 29 vimos sus cuatro vistas de los dispositivos. El directorio completo tiene algunas ramas más:

DirectorioContenidoTema
/sys/devicesTodos los dispositivos, organizados según su conexión física.29
/sys/bus, /sys/class, /sys/blockVistas del anterior: por bus, por función, y los de bloques.8 y 29
/sys/moduleLos módulos cargados y sus parámetros.24
/sys/firmwareLo que el firmware entregó al kernel: tablas ACPI, variables EFI.20
/sys/kernelAjustes y datos internos del kernel.—
/sys/powerEl control de la suspensión y el apagado.—
/sys/fsDatos de los sistemas de archivos y de los grupos de control de procesos.—

La primera fila es el árbol real, y su estructura tiene significado. Siguiendo el enlace de un dispositivo se obtiene su ubicación física:

terminal
$ readlink -f /sys/block/nvme0n1
/sys/devices/pci0000:00/0000:00:01.1/0000:03:00.0/nvme/nvme0/nvme0n1
$ basename $(readlink /sys/block/nvme0n1/device/device/driver)
nvme

La ruta es la cadena de conexiones del disco, de izquierda a derecha: el bus PCI principal, un puente, el controlador en la dirección 03:00.0 —la que mostraba lspci en el tema 8—, y de ahí el dispositivo NVMe y el disco. Cada componente de la ruta es un dispositivo que contiene al siguiente. El segundo comando sigue otro enlace, el que va de un dispositivo al driver que lo maneja.

10. Lo que se puede leer en /sys

Como cada archivo contiene un valor, consultar un dato del hardware se reduce a un cat. Algunos ejemplos, que dependen de lo que tenga cada equipo:

ArchivoContenido
/sys/devices/system/cpu/onlineLos procesadores en funcionamiento, como un rango: 0-7.
/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freqLa frecuencia actual de un procesador, en kHz.
/sys/class/thermal/thermal_zone0/tempUna temperatura, en milésimas de grado.
/sys/class/power_supply/BAT0/capacityLa carga de la batería, en porcentaje.
/sys/class/backlight/*/brightnessEl brillo de la pantalla. Se puede escribir.
/sys/class/net/enp3s0/statistics/rx_bytesLos bytes recibidos por una interfaz de red.
/sys/block/nvme0n1/queue/schedulerEl planificador de pedidos del disco. Se puede escribir.
terminal
$ cat /sys/devices/system/cpu/online
0-7
$ cat /sys/class/thermal/thermal_zone0/temp
41000
$ cat /sys/class/net/enp3s0/statistics/rx_bytes; sleep 1; cat /sys/class/net/enp3s0/statistics/rx_bytes
1840226113
1840391874

Cuarenta y un grados, y unos 165 KB recibidos por la red en un segundo. El último ejemplo aplica el método de la sección 6: un contador que solo crece, leído dos veces.

Los archivos con permiso de escritura reemplazan a muchas operaciones que antes requerían ioctl. Cambiar el brillo de la pantalla es escribir un número en un archivo, algo que cualquier script puede hacer.

Según el equipo

El contenido de /sys refleja el hardware presente. Un equipo de escritorio no tiene batería; una máquina virtual tiene pocos sensores; en WSL, el árbol de dispositivos es mínimo. Un script que lee estos archivos debe comprobar antes que existan. /proc, en cambio, es prácticamente igual en todos los entornos.

11. Leerlos bien

Los dos directorios se leen con las herramientas de siempre, pero no son archivos comunes, y hay algunas reglas que evitan errores sutiles:

ReglaMotivo
Leer el archivo completo, de una vez.El kernel genera el contenido al leer. Si se lo lee en varios pedazos con pausas entre ellos, las partes pueden corresponder a instantes distintos.
No confiar en el tamaño.Es cero. No sirve reservar memoria según lo que informe stat.
Buscar los campos por nombre.Las versiones nuevas del kernel añaden líneas y columnas.
Prever que el archivo no exista.Un proceso puede haber terminado; un dispositivo puede no estar en ese equipo.
Atender a las unidades.Kilobytes en meminfo, centésimas de segundo en stat, páginas en algunos campos, milésimas de grado en los sensores.
Para una tasa, leer dos veces.Casi todos los valores son contadores acumulados desde el arranque.
No leer más seguido de lo necesario.Cada lectura ejecuta código del kernel.

Los programas de este tema las respetan. usocpu.c lee una línea entera con una sola llamada a fscanf, y vuelve a abrir el archivo en cada medición en lugar de reposicionarse. procesos.sh ignora los directorios que desaparecen.

La primera regla tiene una excepción inevitable. ps no puede leer todos los procesos en un mismo instante: los recorre uno por uno. Lo que muestra no es una fotografía exacta, sino un barrido, y por eso los porcentajes de una columna pueden sumar algo más de cien.

12. Las herramientas son lectores

La consecuencia más útil de este tema es que casi todos los comandos de diagnóstico que usamos en el curso son, por dentro, programas que leen estos archivos y dan formato a lo que encuentran:

ComandoLee
ps, top, pgrep, pstree/proc/PID/stat, status, cmdline
free/proc/meminfo
uptime/proc/uptime, /proc/loadavg
vmstat/proc/stat, /proc/meminfo, /proc/vmstat
nproc, lscpu/proc/cpuinfo, /sys/devices/system/cpu
lsmod/proc/modules
findmnt, df/proc/self/mountinfo
lsblk/sys/block, /sys/dev/block
lspci, lsusb/sys/bus/pci, /sys/bus/usb
pmap/proc/PID/maps, smaps

No hace falta creer en la tabla. La herramienta del tema 16 permite comprobarla con cualquier comando:

terminal
$ strace -e trace=openat free 2>&1 | grep -E '/proc|/sys'
openat(AT_FDCWD, "/proc/meminfo", O_RDONLY) = 3
$ strace -e trace=openat uptime 2>&1 | grep -E '/proc|/sys'
openat(AT_FDCWD, "/proc/uptime", O_RDONLY) = 3
openat(AT_FDCWD, "/proc/loadavg", O_RDONLY) = 3

De esto se sigue una manera de trabajar. Cuando un comando no muestra lo que se necesita, o hace falta el dato dentro de un programa, se averigua con strace de qué archivo lo saca, y se lee ese archivo. Ninguna de estas herramientas tiene un acceso privilegiado: todo lo que saben está a disposición de cualquier programa.

Es lo que haremos en el tema 33, el proyecto del curso: un inspector del sistema que reúne, en C y en Bash, lecturas como las de este tema.

13. Límites, y cierre del bloque

La interfaz de archivos es cómoda y tiene sus costos. Conviene conocerlos para no pedirle lo que no puede dar:

Para lo que esta interfaz no resuelve bien existen otras: canales por los que el kernel envía avisos a los programas, como el que usa udev en el tema 29, y las herramientas basadas en eBPF del tema 24, que recogen los datos dentro del propio kernel.

Con este tema termina el bloque de servicios. Recorrimos los cinco, y cada uno resultó ser una abstracción construida sobre los mecanismos de los bloques anteriores:

ServicioAbstracción que ofreceSe apoya enTema
ProcesosUn procesador para cada programaEl temporizador y el cambio de contexto26
MemoriaUna memoria propia, amplia y continuaLa traducción de direcciones y los fallos de página27
ArchivosDatos con nombre, que persistenLos dispositivos de bloques y la caché28
DispositivosHardware diverso tras una interfaz comúnLos drivers y las interrupciones29
ProtecciónCada uno accede solo a lo suyoLos modos de ejecución y la validación en las llamadas30

Y /proc y /sys son la ventana por la que se observa a los cinco. Queda el bloque final, que no añade conceptos nuevos: usa los que tenemos. En el tema 32 seguimos un solo comando, desde que se pulsa Enter hasta que su salida aparece en pantalla, pasando por todos los temas del curso.

14. Resumen

15. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué ventaja tiene exponer la información del kernel como archivos, en lugar de con una llamada al sistema por dato?

Se puede consultar con cualquier herramienta de texto y desde cualquier lenguaje, sin funciones especiales. Y añadir un dato nuevo no requiere crear una llamada al sistema ni modificar las bibliotecas.

2. /proc/uptime tiene tamaño cero y, al leerlo, entrega texto. ¿De dónde sale?

De una función del kernel asociada a ese archivo. Al recibir la llamada read, obtiene el tiempo actual, le da formato de texto y lo devuelve. No hay nada almacenado.

3. ¿Por qué el contenido de estos archivos está siempre actualizado?

Porque se genera en el momento de cada lectura, a partir de las tablas del kernel. No existe una copia que pueda quedar atrasada.

4. ¿Qué diferencia hay entre los archivos status y stat de un proceso?

Contienen información parecida. status la presenta en líneas con nombre, legibles para una persona. stat la presenta en una sola línea de números, más rápida de interpretar para un programa.

5. ¿A qué apunta /proc/self?

Al directorio del proceso que lo está leyendo. Permite que un programa consulte sus propios datos sin averiguar su PID.

6. En procesos.sh, ¿por qué se descartan los errores al leer cada directorio?

Porque un proceso puede terminar entre el momento en que se obtiene la lista de directorios y el momento en que se lee el suyo. Su directorio desaparece, y la lectura falla sin que haya ningún problema.

7. ¿Por qué los nombres de proceso aparecen cortados a quince caracteres?

Porque el kernel guarda el nombre en un campo de dieciséis bytes de la estructura del proceso: quince caracteres y el terminador.

8. ¿En qué archivo está el porcentaje de uso de la CPU?

En ninguno. /proc/stat contiene tiempos acumulados desde el arranque. El porcentaje se calcula leyéndolo dos veces y dividiendo la diferencia del tiempo ocupado por la diferencia del tiempo total.

9. ¿Por qué conviene buscar MemAvailable por su nombre y no por su número de línea?

Porque las versiones nuevas del kernel añaden líneas al archivo, y la posición de cada una puede cambiar. El nombre se mantiene.

10. ¿Qué hace sysctl -w vm.swappiness=10, y cuánto dura su efecto?

Escribe el valor 10 en /proc/sys/vm/swappiness, lo que cambia de inmediato ese parámetro del kernel. Dura hasta el reinicio, porque es una variable en memoria.

11. ¿Por qué no funciona sudo echo 60 > /proc/sys/vm/swappiness?

Porque la redirección la realiza la shell, con los permisos del usuario común, antes de ejecutar sudo. El archivo no puede abrirse para escritura. Con tee, lo abre un programa que ya corre como administrador.

12. ¿Cuáles son las tres reglas de organización de /sys?

Cada objeto del kernel es un directorio; cada atributo es un archivo con un solo valor; y las relaciones entre objetos se expresan con enlaces simbólicos.

13. ¿Qué información da la ruta completa de un dispositivo dentro de /sys/devices?

La cadena de conexiones físicas: cada componente de la ruta es un dispositivo que contiene al siguiente, desde el bus principal hasta el dispositivo en cuestión.

14. ¿Cómo se averigua de dónde obtiene sus datos un comando como free?

Ejecutándolo bajo strace -e trace=openat y observando qué archivos de /proc o /sys abre.

15. Menciona dos limitaciones de esta interfaz.

Es propia de Linux, de modo que los programas que la usan no son portables. Y solo responde cuando se la consulta: no avisa de los cambios, lo que obliga a preguntar repetidamente. También tiene el costo de convertir los datos a texto y volver a interpretarlos.

Para practicar

  1. Lee /proc/uptime y convierte el primer valor a horas y minutos. Compáralo con la salida de uptime.
  2. Añade a procesos.sh una columna con la memoria física de cada proceso, tomada de la línea VmRSS de status. ¿Por qué algunos procesos no tienen esa línea?
  3. Compara el tiempo de time ./procesos.sh > /dev/null con el de time ps -e > /dev/null. Cuenta con strace -c -f cuántos procesos crea el script.
  4. Modifica usocpu.c para que muestre por separado el porcentaje en modo usuario y en modo kernel. Pruébalo con ./tiempos calcular y con ./tiempos pedir.
  5. Amplía el programa anterior para que informe el uso de cada procesador, leyendo las líneas cpu0, cpu1 y siguientes. Lanza taskset -c 2 ./bucle y comprueba cuál se ocupa.
  6. Escribe una orden con awk que muestre, a partir de /proc/meminfo, cuántos megabytes ocupa la caché de disco.
  7. Lista todos los parámetros con sysctl -a 2>/dev/null | wc -l. Busca los que contienen la palabra sched y averigua en man 5 proc qué hace alguno.
  8. Sigue con readlink -f la ruta en /sys/devices de tu disco y de tu tarjeta de red. ¿En qué punto se separan las dos rutas?
  9. Escribe un script que muestre cada segundo los bytes recibidos y enviados por tu interfaz de red en ese segundo, usando los archivos de statistics.
  10. Averigua con strace qué archivos leen nproc, lsblk y lscpu.