1. Qué es un PID
El PID (process identifier) es un número entero que el núcleo asigna a cada proceso en el momento de crearlo. Mientras el proceso vive, ese número lo identifica de forma única en el sistema: con él se lo nombra en las herramientas de observación, se le envían señales, se consultan sus recursos y se lo espera. Cuando el proceso termina y su padre recoge su estado, el número queda libre y puede reutilizarse para un proceso nuevo.
El PID es, en cierto sentido, el nombre público del proceso. Todo lo que el sistema hace con un proceso pasa por su PID: la shell usa el PID para esperar a un hijo, kill lo usa para enviar una señal, ps lo muestra en su primera columna, y el propio proceso puede consultarlo para saber quién es. Por eso, en un programa que crea procesos, el primer paso suele ser preguntar el PID, para saber si el código que se está ejecutando es el proceso original o uno nuevo.
Cada proceso tiene también un PPID (parent process identifier): el PID del proceso que lo creó. El PPID no cambia durante la vida del proceso, salvo en un caso especial que veremos en el bloque 6: cuando el padre termina antes que el hijo, el hijo queda huérfano y el núcleo le asigna un nuevo padre, cuyo PID se convierte en su nuevo PPID.
Con saber el PID de un proceso y el de su padre ya se puede ubicar en el árbol del sistema: todo proceso cuelga de otro, y la cadena de padres lleva hasta el proceso 1. El tema 4 profundiza en el bloque de control de proceso, donde el núcleo guarda muchos más datos; pero PID y PPID son los que se usan todos los días.
2. getpid y getppid en C
POSIX ofrece dos funciones, declaradas en <unistd.h>, para consultar la identidad del proceso que las llama:
| Función | Devuelve | significado |
|---|---|---|
pid_t getpid(void) | El PID del proceso que la llama | «¿Quién soy yo?» |
pid_t getppid(void) | El PID del padre del proceso que la llama | «¿Quién me creó?» |
Ambas devuelven un pid_t, un tipo entero cuyo tamaño exacto no fija el estándar POSIX. En linux es un int de 32 bits, pero el estándar solo garantiza que puede representar cualquier PID válido. Por eso, al imprimirlo con printf, se convierte a int y se usa %d. Las funciones no fallan: no tienen caso de error, y siempre devuelven un valor válido.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Soy el proceso %d\n", (int) getpid());
printf("Mi padre es el proceso %d\n", (int) getppid());
return 0;
}
$ gcc -std=c17 -Wall -Wextra -o identidad identidad.c
$ ./identidad
Soy el proceso 612
Mi padre es el proceso 402
$ echo $$
402
El PID del proceso es 612, y su padre es 402, que coincide con el PID de la shell actual. Eso confirma lo que se vio en el tema 1: cuando lanzas un programa desde la terminal, la shell crea un proceso hijo para ejecutarlo, y ese proceso tiene como padre a la propia shell.
La variable especial $$ de la shell expande al PID de la shell, y $! al PID del último proceso lanzado en segundo plano. Son atajos útiles para scripts:
$ echo "shell: $$"
shell: 402
$ sleep 100 &
[1] 615
$ echo "último hijo: $!"
último hijo: 615
$ kill $!
3. Cómo asigna el núcleo los PIDs
El núcleo no elige los PIDs al azar. Los asigna en orden creciente, empezando por el 1, y cuando llega al máximo configurado vuelve a empezar por el número más bajo que esté libre. Cada vez que se crea un proceso (con fork, clone o las llamadas equivalentes) el núcleo busca el próximo número libre y se lo asigna.
Esto tiene varias consecuencias que conviene tener presentes:
- Los PIDs son únicos solo mientras el proceso vive. Dos procesos distintos nunca tienen el mismo PID al mismo tiempo, pero un PID puede reutilizarse para un proceso nuevo después de que el anterior terminó y su padre recogió su estado.
- El orden de los PIDs refleja el orden de creación. Si lanzas dos programas seguidos, el segundo tendrá un PID mayor que el primero. Es una pista útil para entender que se creó antes.
- Los PIDs no dicen nada del programa. El número no codifica el tipo de programa, ni el usuario, ni la prioridad. Es solo un identificador.
- El núcleo reserva los números bajos. El PID 1 es el primer proceso del sistema, y varios números bajos se asignan a procesos del núcleo y a servicios del arranque. Los procesos de usuario suelen tener PIDs de cuatro o cinco dígitos.
El algoritmo exacto de asignación está en el código del núcleo (la función alloc_pid), y optimiza para no reutilizar un número recién liberado demasiado pronto, para evitar confusiones con procesos que aún puedan estar referenciados. En la práctica, en un sistema normal, los PIDs suben hasta el límite y luego vuelven a empezar.
Un programa no debería tomar decisiones basándose en su propio PID ni en el de otro proceso. El número es un identificador, no un dato semántico. Si necesitas saber que programa es un proceso, mira su nombre en /proc/PID/comm o la línea de comandos en /proc/PID/cmdline, no el número.
4. El árbol de procesos y el proceso 1
Cómo cada proceso tiene un padre, el conjunto de procesos del sistema forma un árbol: la raíz es el proceso 1, y cada proceso cuelga de su padre. Ese árbol es la estructura fundamental del sistema, y se puede dibujar con pstree.
El proceso 1 es el primero que crea el núcleo al arrancar. En los sistemas modernos basados en systemd, ese proceso es systemd; en sistemas más antiguos o minimalistas, puede ser init. Su PID es siempre 1, y es el antepasado de todos los demás procesos. Cuando un proceso queda huérfano (su padre termina antes que él), el núcleo le reasigna como padre al proceso 1, o a un subreaper que haya tomado ese rol. El proceso 1 tiene la responsabilidad de recoger los procesos huérfanos que terminan, para que no queden como zombis.
El proceso 0, que no aparece en las herramientas habituales, es el proceso inactivo (idle) del núcleo: no es un proceso normal, sino el contexto que se ejecuta cuando no hay ningún proceso listo para correr. Por eso los PIDs de usuario empiezan en 1.
5. Observar procesos con ps
ps (process status) es la herramienta clásica para listar procesos. Por omisión muestra solo los procesos de la terminal actual, pero con opciones se puede ver todo el sistema. Las dos formas más usadas son:
$ ps
PID TTY TIME CMD
402 pts/0 00:00:00 bash
612 pts/0 00:00:00 ps
Para ver todos los procesos del sistema con información detallada se usa ps aux o ps -ef. Las dos muestran PID y PPID, pero con columnas distintas:
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 168960 12832 ? Ss 19:30 0:01 /sbin/init
root 151 0.0 0.0 8500 3900 ? Ss 19:30 0:00 /usr/sbin/cron
ana 402 0.0 0.1 8500 4500 pts/0 Ss 19:40 0:00 -bash
ana 613 0.0 0.0 9200 3200 pts/0 R+ 19:45 0:00 ps aux
$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 19:30 ? 00:00:00 /sbin/init
root 151 1 0 19:30 ? 00:00:00 /usr/sbin/cron
ana 402 332 0 19:40 pts/0 00:00:00 -bash
ana 614 402 0 19:45 pts/0 00:00:00 ps -ef
La diferencia clave: ps -ef muestra el PPID de forma explícita, lo que permite ver la relación padre-hijo de un vistazo. ps aux muestra el uso de CPU y memoria, pero no el PPID directamente (aunque se puede pedir con ps auxo pid,ppid,...).
Para pedir columnas específicas se usa la opción -o, con una lista separada por comas. Es la forma más flexible y la que más se usa en scripts:
$ ps -o pid,ppid,stat,etime,comm -p 1,402
PID PPID STAT ELAPSED COMMAND
1 0 Ss 00:15:32 systemd
402 332 Ss 00:05:12 bash
Las columnas más útiles para identificar procesos:
| Columna | significado |
|---|---|
PID | identificador del proceso. |
PPID | identificador del padre. |
UID / USER | Usuario que ejecuta el proceso. |
STAT | Estado: R ejecutándose, S dormido, D espera de disco, T detenido, Z zombi. El tema 7 los detalla. |
TTY | Terminal de control del proceso, o ? si no tiene. |
TIME / TIME+ | Tiempo de CPU acumulado. |
ELAPSED / etime | Tiempo transcurrido desde que se creó el proceso. |
COMMAND / comm / cmd | Nombre del programa o línea de comandos completa. |
START | Hora en que se creó. |
Para filtrar por nombre de programa se usa -C, y para filtrar por PID exacto, -p. Combinado con --no-headers y wc -l, sirve para contar:
$ ps -C bash -o pid,ppid,comm
PID PPID COMMAND
402 332 bash
520 402 bash
$ ps -e --no-headers | wc -l
47
6. pstree: el árbol completo
pstree dibuja el árbol de procesos del sistema, con cada proceso colgando de su padre. Es la herramienta ideal para entender la estructura del sistema de un vistazo, y para ver que procesos creó un servidor o un demonio.
$ pstree -p
systemd(1)─┬─cron(151)
├─dbus-daemon(152)
├─login(332)───bash(402)─┬─gcc(500)
│ ├─vim(501)
│ └─servidor(502)─┬─worker(503)
│ ├─worker(504)
│ └─worker(505)
└─systemd-journal(44)
Las opciones más útiles:
-pmuestra el PID entre paréntesis junto a cada nombre.-umuestra el usuario cuando difiere del padre.-amuestra la línea de comandos completa.-hresalta el proceso actual y sus ancestros.-s PIDmuestra solo los ancestros de un proceso dado.
$ pstree -s -p 503
systemd(1)───login(332)───bash(402)───servidor(502)───worker(503)
Los nombres entre llaves, como {rsyslogd}, que viste en el tema 1, no son procesos sino hilos dentro de un proceso. pstree los muestra por omisión; con -T se ocultan y solo se ven los procesos.
7. top y htop: monitoreo en tiempo real
Mientras ps toma una fotografía instantánea, top muestra los procesos en tiempo real, actualizándose cada pocos segundos, ordenados por uso de CPU por omisión. Es la herramienta que se usa para ver que está consumiendo la máquina en este momento.
$ top
top - 19:50:00 up 20 min, 1 user, load average: 0.05, 0.02, 0.01
Tasks: 47 total, 1 running, 46 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.3 us, 0.7 sy, 0.0 ni, 96.8 id, 0.2 wa, 0.0 hi, 0.2 si
MiB Mem : 7850.0 total, 6200.0 free, 900.0 used, 750.0 buff/cache
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
618 ana 20 0 9000 3200 2800 R 0.3 0.0 0:00.01 top
402 ana 20 0 8500 4500 3900 S 0.0 0.1 0:00.05 bash
Las teclas más útiles dentro de top:
Pordena por uso de CPU,Mpor uso de memoria,Tpor tiempo acumulado.kenvía una señal a un proceso (pide el PID).rcambia la prioridad (nice) de un proceso.1muestra cada núcleo de CPU por separado.Hmuestra los hilos como si fueran procesos.qsale.
htop es una versión mejorada, con colores, barras de uso por núcleo y navegación con el teclado y el ratón. No viene instalado por omisión en todos los sistemas, pero se instala con apt install htop. Ambas herramientas leen los mismos datos de /proc que ps, así que los PIDs coinciden.
8. /proc y los identificadores
Cómo viste en los temas anteriores, el núcleo publica los datos de cada proceso en /proc/PID. Para identificar un proceso, los archivos más directos son:
| Archivo | Que contiene |
|---|---|
/proc/PID/status | Entre otras cosas, las líneas Name, Pid, PPid, Tgid y Threads. |
/proc/PID/stat | Una sola línea con todos los campos del proceso, incluido el PID y el PPID, en formato compacto para programas. |
/proc/PID/cmdline | La línea de comandos con la que se lanzó, con los argumentos separados por bytes nulos. |
/proc/PID/comm | El nombre corto del programa (hasta 15 caracteres). |
/proc/PID/exe | Enlace al archivo ejecutable. |
/proc/self | Enlace al directorio del proceso que lo lee. |
$ grep -E '^(Name|Pid|PPid|Tgid|Threads)' /proc/402/status
Name: bash
Pid: 402
PPid: 332
Tgid: 402
Threads: 1
$ cat /proc/402/comm
bash
$ tr '\0' ' ' < /proc/402/cmdline; echo
-bash
El campo Tgid (thread group id) es el identificador del grupo de hilos al que pertenece el proceso. En un proceso con un solo hilo, el tgid coincide con el PID. En un proceso con varios hilos, todos los hilos comparten el mismo tgid, que es el PID del proceso. Esta distinción es la que permite diferenciar procesos de hilos, y la veremos en el apartado siguiente.
9. Encontrar procesos: pgrep y pidof
Cuando se necesita el PID de un proceso por su nombre, en lugar de leer ps y filtrar, se usan pgrep y pidof:
$ pgrep bash
402
520
$ pgrep -a bash
402 -bash
520 bash
$ pidof bash
520 402
pgrep busca procesos por nombre (o por otros criterios con opciones como -u para usuario, -f para buscar en la línea de comandos completa, -x para coincidencia exacta) y devuelve solo los PIDs, uno por línea. Es ideal para usar en scripts:
$ for p in $(pgrep -f "sleep 100"); do echo "matando $p"; kill "$p"; done
pidof es más simple: busca por nombre exacto del programa y devuelve todos los PIDs en una sola línea. Es útil cuando se conoce el nombre exacto y se quiere pasar la lista a otra herramienta, como kill.
10. El límite de PIDs y pid_max
El número máximo de PIDs que el núcleo puede asignar está en /proc/sys/kernel/pid_max. En un sistema de 64 bits el valor por omisión suele ser 4194304 (222); en sistemas de 32 bits, 32768. Se puede consultar y cambiar (con privilegios):
$ cat /proc/sys/kernel/pid_max
4194304
$ echo 100000 | sudo tee /proc/sys/kernel/pid_max
Cuando el núcleo llega a pid_max, vuelve a empezar por el número más bajo libre. Si todos los números están en uso (algo que solo ocurre en sistemas con millones de procesos o con un límite muy bajo), la creación de nuevos procesos falla con EAGAIN. Es el mismo error que aparece en una fork bomb, que veremos en el tema 15.
Hay también un límite por usuario: ulimit -u muestra el número máximo de procesos que un usuario puede tener a la vez. Es una protección contra que un usuario agote los recursos del sistema:
$ ulimit -u
31263
11. Hilos y PIDs: TID frente a PID
En linux, los hilos se implementan como procesos ligeros que comparten memoria. Cada hilo tiene su propio identificador, el TID (thread identifier), que el núcleo asigna con la misma secuencia que los PIDs. Pero todos los hilos de un mismo proceso comparten el mismo PID desde el punto de vista del usuario: ese PID es el del proceso que los contiene, y se llama TGID (thread group id).
Por eso, cuando un programa con varios hilos llama a getpid(), todos los hilos obtienen el mismo número: el PID del proceso. En cambio, si un hilo quiere saber su propio TID, debe usar una llamada específica del sistema, gettid(), que no tiene envoltura en la biblioteca de C y se invoca con syscall(SYS_gettid):
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <pthread.h>
void *trabajo(void *arg)
{
printf("hilo: PID=%d TID=%ld\n",
(int) getpid(), (long) syscall(SYS_gettid));
return NULL;
}
int main(void)
{
pthread_t t;
printf("main: PID=%d TID=%ld\n",
(int) getpid(), (long) syscall(SYS_gettid));
pthread_create(&t, NULL, trabajo, NULL);
pthread_join(t, NULL);
return 0;
}
$ gcc -std=c17 -Wall -Wextra -pthread -o hilos_id hilos_id.c
$ ./hilos_id
main: PID=701 TID=701
hilo: PID=701 TID=702
El hilo principal tiene TID igual al PID del proceso (701). El hilo creado tiene un TID distinto (702), pero el mismo PID. Por eso, en /proc, cada hilo aparece como un directorio numerado con su TID, pero todos comparten el mismo espacio de memoria y el mismo PID desde el punto de vista del usuario. El tema 29 profundiza en los modelos de hilos y en como linux los implementa con clone.
Por eso, al listar procesos con ps, por omisión solo se ven los hilos principales. Para ver todos los hilos se usa ps -L o top -H:
$ ps -L -p 701 -o pid,tid,comm
PID TID COMMAND
701 701 hilos_id
701 702 hilos_id
12. Un programa que se identifica
El siguiente programa muestra como un proceso puede consultar su identidad, la de su padre, y verificarla contra /proc. Es útil para entender la relación entre lo que el programa ve y lo que el sistema muestra:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pid = getpid();
pid_t ppid = getppid();
printf("PID propio: %d\n", (int) pid);
printf("PID del padre:%d\n", (int) ppid);
/* verificar contra /proc */
char ruta[64];
snprintf(ruta, sizeof ruta, "/proc/%d/status", (int) pid);
FILE *f = fopen(ruta, "r");
if (f) {
char linea[256];
while (fgets(linea, sizeof linea, f)) {
if (strncmp(linea, "Name:", 5) == 0 ||
strncmp(linea, "Pid:", 4) == 0 ||
strncmp(linea, "PPid:", 5) == 0) {
fputs(linea, stdout);
}
}
fclose(f);
}
printf("\ncomprueba con: ps -p %d -o pid,ppid,comm\n", (int) pid);
return 0;
}
Al ejecutarlo, la salida del programa coincide con lo que /proc reporta, y el comando sugerido al final permite verificarlo desde otra terminal. Es un buen ejercicio para confirmar que el proceso ve la misma identidad que el sistema le asigna.
13. Resumen
- El PID es el número que identifica de forma única a un proceso mientras vive; el PPID es el PID de su padre.
getpid()ygetppid()devuelven el PID y el PPID del proceso que las llama; devuelvenpid_ty no fallan.- El núcleo asigna PIDs en orden creciente y los reutiliza cuando se liberan; el valor no codifica nada sobre el programa.
- El proceso 1 (systemd o init) es la raíz del árbol de procesos y el antepasado de todos los demás.
pslista procesos;ps -efmuestra PID y PPID;ps -opermite elegir columnas.pstreedibuja el árbol de procesos;-pmuestra los PIDs,-slos ancestros.topyhtopmonitorean en tiempo real;top -Hmuestra los hilos./proc/PID/statusexponePid,PPidyTgid;/proc/PID/commel nombre corto.pgrepypidofencuentran PIDs por nombre de programa.- El límite de PIDs está en
/proc/sys/kernel/pid_max, y el límite por usuario enulimit -u. - Los hilos comparten el PID del proceso (TGID) pero tienen TID propio;
gettid()devuelve el TID.
14. Preguntas de repaso
Intenta responder cada una antes de abrir la respuesta.
1. ¿Que es el PID de un proceso?
Es el número entero que el núcleo asigna a un proceso al crearlo y que lo identifica de forma única mientras vive. Cuando el proceso termina y su padre recoge su estado, el número se libera y puede reutilizarse.
2. ¿Que devuelve getppid()?
El PID del proceso que creó al proceso que la llama: su padre. No cambia durante la vida del proceso, salvo cuando el padre termina antes y el hijo queda huérfano, en cuyo caso el núcleo le asigna un nuevo padre.
3. ¿Por que no conviene tomar decisiones basándose en el valor del PID?
Porque el PID es solo un identificador numérico: no codifica el tipo de programa, el usuario ni la prioridad, y se reutiliza. Para saber que programa es un proceso hay que mirar su nombre o su línea de comandos en /proc.
4. ¿Que proceso tiene PID 1, y por que es especial?
El proceso 1 es el primero que crea el núcleo al arrancar (systemd o init). Es la raíz del árbol de procesos, antepasado de todos los demás, y el encargado de recoger los procesos huérfanos que terminan.
5. ¿Que opción de ps muestra el PPID de cada proceso?
ps -ef lo muestra en la columna PPID. También se puede pedir con ps -o pid,ppid,comm o agregarlo a ps aux con ps axo user,pid,ppid,...,comm.
6. ¿Para que sirve pstree -s PID?
Muestra la cadena de ancestros del proceso indicado: desde el proceso 1 hasta ese proceso, pasando por cada padre. Es útil para entender de dónde vino un proceso.
7. ¿Que diferencia hay entre PID y TID?
El PID identifica al proceso (el grupo de hilos) y es el que devuelven getpid() y las herramientas de usuario. El TID identifica a un hilo individual dentro del proceso. Todos los hilos de un proceso comparten el mismo PID (TGID) pero tienen TID distintos.
8. ¿Como se obtiene el TID de un hilo en C?
Con syscall(SYS_gettid), incluyendo <sys/syscall.h>, porque la biblioteca de C no ofrece una envoltura para gettid. El hilo principal tiene TID igual al PID del proceso.
9. ¿Que es pid_max y dónde se consulta?
Es el número máximo de PIDs que el núcleo puede asignar. Se consulta en /proc/sys/kernel/pid_max, y cuando se alcanza, el núcleo vuelve a empezar por el número más bajo libre.
10. ¿Que muestra pgrep -a que no muestra pgrep solo?
La línea de comandos completa de cada proceso encontrado, además del PID. Sin -a, pgrep devuelve solo los números.
Para practicar
- Ejecuta
identidad.cy verifica conps -p PID -o pid,ppid,commque el PPID coincide con el de la shell. - Lanza un programa en segundo plano y usa
echo $!para obtener su PID; luego míralo conpsy/proc/PID/status. - Dibuja el árbol de procesos de tu sistema con
pstree -py encuentra el camino desde el proceso 1 hasta la shell en la que trabajas. - Usa
pgrep -fpara encontrar todos los procesos que ejecutan un comando que contenga una palabra determinada. - Compila y ejecuta
hilos_id.c, y mientras corre, miraps -L -p PIDy/proc/PID/task. - Consulta
cat /proc/sys/kernel/pid_maxyulimit -uen tu sistema, y compáralos con los valores del texto.