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 dato | Archivos | |
|---|---|---|
| El programa | Llama a una función específica y recibe una estructura | Abre un archivo y lee texto |
| Ejemplos que usamos | uname, getpid, getrusage, sysconf | /proc/meminfo, /proc/interrupts |
| Añadir un dato nuevo | Requiere una llamada nueva, y un envoltorio en la biblioteca | Requiere un archivo nuevo, o una línea más |
| Se puede usar desde | Un lenguaje que tenga acceso a esa llamada | Cualquier lenguaje, y la shell |
| Costo | Mínimo | Dar 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 | |
|---|---|---|
| Origen | Años ochenta; en Linux, desde el principio | 2003 |
| Contenido principal | Los procesos, y estadísticas generales del sistema | Los dispositivos, los drivers y la configuración del kernel |
| Formato | Variado: cada archivo, el suyo | Uniforme: un valor por archivo |
| Organización | Creció por acumulación | Un á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.
$ 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.
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:
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:
- El contenido es siempre actual. No hay nada que pueda quedar desactualizado, porque no hay nada guardado.
- No ocupan espacio. Ni en el disco ni, salvo durante la lectura, en la memoria.
- Leer no es gratis. Cada lectura ejecuta código del kernel. Algunos archivos, que deben recorrer miles de estructuras, son costosos.
3. El mapa de /proc
El directorio tiene dos clases de entradas, que se distinguen a simple vista:
$ 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:
| Archivo | Contenido | Lo usamos en |
|---|---|---|
version, cmdline | La versión del kernel y los parámetros con que arrancó. | Temas 11 y 21 |
uptime, loadavg | El tiempo desde el arranque y la carga media. | Temas 11 y 26 |
cpuinfo | La descripción de cada procesador. | Tema 2 |
stat | Contadores globales: tiempo de CPU por modo, interrupciones, cambios de contexto. | Temas 12, 17 y 19 |
meminfo | El estado de la memoria. | Temas 5, 7 y 27 |
interrupts, softirqs | Los contadores de interrupciones, por origen y por procesador. | Tema 17 |
ioports, iomem | El mapa de puertos y de memoria de los dispositivos. | Tema 8 |
devices | Los drivers registrados, con su número mayor. | Tema 29 |
modules | Los módulos cargados. Es lo que muestra lsmod. | Tema 24 |
kallsyms | Los símbolos del kernel, con sus direcciones. | Tema 11 |
mounts, filesystems | Los sistemas de archivos montados, y los tipos que el kernel conoce. | Tema 28 |
partitions, diskstats | Los 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:
| Entrada | Contenido | Lo usamos en |
|---|---|---|
status | Los datos principales, en formato legible: nombre, estado, identificadores, memoria, cambios de contexto, capacidades. | Temas 11, 19, 27 y 30 |
stat | Los mismos datos y más, en una sola línea de números. Es lo que leen ps y top. | — |
comm, cmdline | El nombre del programa, y los argumentos con que se lanzó. | Tema 26 |
exe, cwd, root | Enlaces al ejecutable, al directorio actual y al directorio raíz del proceso. | Tema 26 |
environ | Sus variables de entorno. | — |
maps, smaps_rollup | El 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 |
limits | Los límites de recursos: tamaño de pila, archivos abiertos, memoria. | Tema 27, como ulimit |
io | Cuá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:
$ 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:
#!/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
$ 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.
$ 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ón | Valor | Tiempo… | En top |
|---|---|---|---|
| 1 | 310452 | ejecutando programas, en modo usuario | us |
| 2 | 1204 | ejecutando programas de baja prioridad | ni |
| 3 | 118730 | en modo kernel, atendiendo llamadas al sistema | sy |
| 4 | 14709201 | sin nada que hacer | id |
| 5 | 20311 | sin nada que hacer, con operaciones de disco pendientes | wa |
| 6 y 7 | 0 y 8102 | atendiendo interrupciones | hi, si |
| 8 | 0 | cedido por el hipervisor a otras máquinas virtuales | st |
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:
#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;
}
$ 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:
$ 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ínea | Significado | Tema |
|---|---|---|
MemTotal | La memoria física que administra el kernel. | 27 |
MemFree | La que no se usa para nada. | 7 |
MemAvailable | La que podrían obtener programas nuevos: la libre más la caché recuperable. | 7 y 27 |
Buffers, Cached | La caché de disco. | 7 |
SwapTotal, SwapFree | El área de intercambio, y cuánto queda sin usar. | 7 |
Dirty | Datos modificados en memoria que aún no se escribieron en el disco. | 7 y 28 |
Committed_AS | La 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:
$ 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.
$ 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ámetro | Controla | Tema |
|---|---|---|
kernel.pid_max | El mayor PID que se asigna. | 26 |
kernel.randomize_va_space | La aleatoriedad del espacio de direcciones. | 27 |
kernel.yama.ptrace_scope | Quién puede observar un proceso con ptrace. | 16 |
kernel.tainted | Si se cargó código ajeno en el kernel. Solo lectura. | 24 |
vm.drop_caches | Al escribir en él, el kernel vacía la caché de disco. | 7 |
vm.overcommit_memory | La política de sobrecompromiso de memoria. | 27 |
vm.swappiness | La inclinación del kernel a usar el intercambio. | — |
Modificar un parámetro requiere ser administrador, y hay dos formas equivalentes:
$ 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:
- Cada objeto del kernel es un directorio: un dispositivo, un driver, un bus, un módulo.
- Cada atributo es un archivo, con un solo valor. No hay tablas ni formatos que interpretar.
- Las relaciones son enlaces simbólicos: de un dispositivo a su driver, de una partición a su disco.
En el tema 29 vimos sus cuatro vistas de los dispositivos. El directorio completo tiene algunas ramas más:
| Directorio | Contenido | Tema |
|---|---|---|
/sys/devices | Todos los dispositivos, organizados según su conexión física. | 29 |
/sys/bus, /sys/class, /sys/block | Vistas del anterior: por bus, por función, y los de bloques. | 8 y 29 |
/sys/module | Los módulos cargados y sus parámetros. | 24 |
/sys/firmware | Lo que el firmware entregó al kernel: tablas ACPI, variables EFI. | 20 |
/sys/kernel | Ajustes y datos internos del kernel. | — |
/sys/power | El control de la suspensión y el apagado. | — |
/sys/fs | Datos 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:
$ 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:
| Archivo | Contenido |
|---|---|
/sys/devices/system/cpu/online | Los procesadores en funcionamiento, como un rango: 0-7. |
/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq | La frecuencia actual de un procesador, en kHz. |
/sys/class/thermal/thermal_zone0/temp | Una temperatura, en milésimas de grado. |
/sys/class/power_supply/BAT0/capacity | La carga de la batería, en porcentaje. |
/sys/class/backlight/*/brightness | El brillo de la pantalla. Se puede escribir. |
/sys/class/net/enp3s0/statistics/rx_bytes | Los bytes recibidos por una interfaz de red. |
/sys/block/nvme0n1/queue/scheduler | El planificador de pedidos del disco. Se puede escribir. |
$ 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.
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:
| Regla | Motivo |
|---|---|
| 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:
| Comando | Lee |
|---|---|
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:
$ 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:
- Es propia de Linux. Como anotamos en el tema 25, macOS no tiene
/proc, y el de otros sistemas tipo Unix es distinto. Un programa que lo usa no es portable. - Convertir a texto y volver cuesta. Para un dato por segundo es irrelevante. Para vigilar cien mil procesos varias veces por segundo, no.
- No avisa. Solo responde cuando se le pregunta. Un programa que quiere enterarse de que un proceso terminó tendría que consultar una y otra vez; es el sondeo del tema 8, con sus inconvenientes.
- Expone información. Las direcciones del kernel o los argumentos de los procesos ajenos pueden ayudar a un atacante. Por eso
/proc/kallsymsmuestra ceros a un usuario común, como vimos en el tema 11.
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:
| Servicio | Abstracción que ofrece | Se apoya en | Tema |
|---|---|---|---|
| Procesos | Un procesador para cada programa | El temporizador y el cambio de contexto | 26 |
| Memoria | Una memoria propia, amplia y continua | La traducción de direcciones y los fallos de página | 27 |
| Archivos | Datos con nombre, que persisten | Los dispositivos de bloques y la caché | 28 |
| Dispositivos | Hardware diverso tras una interfaz común | Los drivers y las interrupciones | 29 |
| Protección | Cada uno accede solo a lo suyo | Los modos de ejecución y la validación en las llamadas | 30 |
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
- Linux expone el estado del kernel como archivos, en
/procy/sys. Se leen con las herramientas comunes y desde cualquier lenguaje. - Esos archivos no están guardados en ningún lado. Cada lectura ejecuta una función del kernel que consulta los datos actuales y los devuelve como texto.
/proccontiene un directorio por proceso, archivos con estadísticas del sistema y, en/proc/sys, parámetros que se pueden modificar./proc/PIDreúne todo lo que el kernel sabe de un proceso;/proc/selflleva al del proceso que lo lee.- Un programa que recorre
/procdebe tolerar que los procesos terminen mientras los examina. /proc/statacumula el tiempo de CPU por modo, en centésimas de segundo. El porcentaje de uso se obtiene restando dos lecturas./proc/meminfoes la fuente defree. Los campos se buscan por nombre.sysctllee y modifica los parámetros de/proc/sys. Los cambios duran hasta el reinicio, salvo que se anoten en/etc/sysctl.d./systiene reglas estrictas: un objeto por directorio, un valor por archivo, y enlaces para las relaciones. Su árbol refleja cómo están conectados los dispositivos.- Casi todos los valores son contadores acumulados: para obtener una tasa hay que leer dos veces.
ps,top,free,lsblky la mayoría de las herramientas de diagnóstico son lectores de estos archivos, ystracemuestra cuáles lee cada una.- La interfaz es propia de Linux, tiene un costo por la conversión a texto y solo responde cuando se la consulta.
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
- Lee
/proc/uptimey convierte el primer valor a horas y minutos. Compáralo con la salida deuptime. - Añade a
procesos.shuna columna con la memoria física de cada proceso, tomada de la líneaVmRSSdestatus. ¿Por qué algunos procesos no tienen esa línea? - Compara el tiempo de
time ./procesos.sh > /dev/nullcon el detime ps -e > /dev/null. Cuenta constrace -c -fcuántos procesos crea el script. - Modifica
usocpu.cpara que muestre por separado el porcentaje en modo usuario y en modo kernel. Pruébalo con./tiempos calculary con./tiempos pedir. - Amplía el programa anterior para que informe el uso de cada procesador, leyendo las líneas
cpu0,cpu1y siguientes. Lanzataskset -c 2 ./bucley comprueba cuál se ocupa. - Escribe una orden con
awkque muestre, a partir de/proc/meminfo, cuántos megabytes ocupa la caché de disco. - Lista todos los parámetros con
sysctl -a 2>/dev/null | wc -l. Busca los que contienen la palabraschedy averigua enman 5 procqué hace alguno. - Sigue con
readlink -fla ruta en/sys/devicesde tu disco y de tu tarjeta de red. ¿En qué punto se separan las dos rutas? - 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. - Averigua con
stracequé archivos leennproc,lsblkylscpu.