Que es un proceso - Procesos e Hilos
← Procesos e Hilos Bloque 1 · El concepto de proceso · Tema 1 de 43

Tema 1

Que es un proceso

Un programa es un archivo quieto en el disco: no hace nada, no ocupa la CPU, no tiene datos que cambien. Cuando lo ejecutas, el sistema operativo construye a su alrededor algo nuevo, con memoria propia, una posición en el código, archivos abiertos y un número que lo identifica. Eso es un proceso. En este tema verás la diferencia en la práctica, ejecutando el mismo programa dos veces a la vez, observarás como varios procesos se reparten una sola CPU, y entenderás por que esta idea es la base sobre la que se construye todo el sistema.

1. Un programa, dos ejecuciones

Empecemos con un programa en C muy corto. Dice quién es, y después incrementa tres veces una variable global, contador, mostrando su valor y la dirección de memoria donde está guardada, con una pausa de un segundo entre cada paso:

proceso.c
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>

int contador = 0;

int main(void)
{
    printf("Soy el proceso %d, hijo de %d\n", (int) getpid(), (int) getppid());

    for (int i = 0; i < 3; i++) {
        contador++;
        printf("[%d] contador = %d, guardado en %p\n",
               (int) getpid(), contador, (void *) &contador);
        sleep(1);
    }
    return 0;
}

Hay dos funciones nuevas, las dos de <unistd.h>: getpid devuelve el número que identifica al proceso que la llama, y getppid, el del proceso que lo creó. Devuelven un pid_t, un tipo entero cuyo tamaño exacto no fija el estándar; por eso se convierte a int antes de mostrarlo con %d. Las veremos en detalle en el tema 3. Lo compilamos y lo ejecutamos una vez:

terminal · una ejecución
$ gcc -std=c17 -Wall -Wextra -o proceso proceso.c
$ ./proceso
Soy el proceso 460, hijo de 331
[460] contador = 1, guardado en 0x579055cbb014
[460] contador = 2, guardado en 0x579055cbb014
[460] contador = 3, guardado en 0x579055cbb014

Nada sorprendente. Pero ahora lo lanzamos dos veces a la vez, las dos en segundo plano con &, y le pedimos a la shell que espere a ambas con wait:

terminal · dos ejecuciones simultáneas del mismo archivo
$ echo $$
579
$ ./proceso & ./proceso & wait
Soy el proceso 580, hijo de 579
[580] contador = 1, guardado en 0x5b3e5f4d6014
Soy el proceso 581, hijo de 579
[581] contador = 1, guardado en 0x5fc139064014
[580] contador = 2, guardado en 0x5b3e5f4d6014
[581] contador = 2, guardado en 0x5fc139064014
[580] contador = 3, guardado en 0x5b3e5f4d6014
[581] contador = 3, guardado en 0x5fc139064014

Mira con atención. Hay un solo archivo, proceso, pero se están ejecutando dos cosas distintas, con números distintos, 580 y 581. Las dos avanzan al mismo tiempo, con sus líneas intercaladas. Y cada una tiene su propio contador: ninguna llega a 6, las dos cuentan 1, 2, 3 por su cuenta, aunque el código dice que contador es una única variable global. Las dos, además, son «hijas» de 579, que según $$ es la propia shell.

Esas dos cosas que se ejecutan son procesos. El resto del curso trata de que son exactamente, como los crea el sistema, como se reparten la máquina y como terminan.

2. Programa y proceso

Un programa es un archivo que contiene instrucciones y datos iniciales, guardado en el disco. Un proceso es un programa en ejecución: una instancia de ese programa, con su propia memoria, su propio estado de la CPU y los recursos que el sistema operativo le asignó, que el núcleo identifica con un número, el PID (process identifier).

La diferencia es la misma que hay entre una receta y alguien cocinándola. La receta es un texto fijo, que puede estar guardado durante años sin que pase nada, y del que pueden hacerse muchas copias. Cocinar es una actividad: tiene ingredientes concretos sobre la mesada, va por un paso determinado, ocupa la cocina durante un tiempo y en algún momento termina. Dos personas pueden seguir la misma receta a la vez en dos cocinas, y cada una tendrá su propia olla.

ProgramaProceso
Que esUn archivo, como /usr/bin/bash o ./procesoUna actividad en curso, que el núcleo administra
Dónde viveEn el discoEn la memoria principal y en la CPU
Cuánto duraHasta que alguien borra el archivoDesde que se crea hasta que termina
CambiaNo: es siempre el mismoSí: sus variables, su posición en el código y su estado cambian a cada instante
Cuántos hayUno por archivoTantos como ejecuciones simultáneas de ese archivo
Se identifica porSu ruta en el sistema de archivosSu PID

Por eso se dice que un programa es una entidad pasiva y un proceso una entidad activa. El archivo proceso del ejemplo sigue en el disco, idéntico, después de que los procesos 580 y 581 terminaron; ellos, en cambio, ya no existen.

3. Lo que el sistema agrega a un programa

Para convertir el archivo en algo que se ejecuta, el núcleo tiene que preparar muchas cosas que el archivo no contiene. Este esquema muestra el mismo programa y los dos procesos del ejemplo:

A la izquierda, el programa proceso en el disco, con su código y sus datos iniciales. Dos flechas, rotuladas ejecutar, llevan a dos procesos, el 580 y el 581. Cada proceso tiene su propia memoria con código, datos con contador, montículo y pila; sus propios registros de la CPU con el contador de programa; sus archivos abiertos, entrada, salida y error estándar; su estado; y su padre, el proceso 579, la shell EN EL DISCO programa ./proceso (ELF) código datos iniciales ejecutar EN MEMORIA, ADMINISTRADOS POR EL NÚCLEO proceso 580 padre: 579 · estado: dormido memoria propia código datos: contador = 2 montículo pila registros de la CPU contador de programa archivos abiertos 0 entrada · 1 salida · 2 error proceso 581 padre: 579 · estado: en ejecución memoria propia código datos: contador = 1 montículo pila registros de la CPU contador de programa archivos abiertos 0 entrada · 1 salida · 2 error
Un programa, dos procesos. El archivo aporta el código y los datos iniciales; todo lo demás lo crea el núcleo, por separado, para cada ejecución.

Cada proceso recibe del sistema, como mínimo:

El núcleo guarda toda esa información sobre cada proceso en una estructura llamada bloque de control de proceso, o PCB, que veremos en el tema 4. Para el sistema operativo, en cierto sentido, un proceso es su PCB: la ficha donde está escrito todo lo que hace falta para detenerlo y reanudarlo exactamente donde estaba.

4. Cada proceso tiene su propia memoria

En la ejecución doble había un detalle llamativo: las direcciones de contador eran distintas en cada proceso, 0x5b3e5f4d6014 y 0x5fc139064014. Podría pensarse que por eso cada uno tiene su propio contador: están en lugares distintos. Pero las direcciones cambian por ASLR, la protección que coloca la memoria de cada proceso en un lugar al azar, que conociste en el curso de C. La orden setarch -R ejecuta un programa con ASLR desactivado, y permite hacer el experimento definitivo:

terminal · los dos procesos sin ASLR
$ setarch -R ./proceso & setarch -R ./proceso & wait
Soy el proceso 536, hijo de 535
[536] contador = 1, guardado en 0x555555558014
Soy el proceso 537, hijo de 535
[537] contador = 1, guardado en 0x555555558014
[536] contador = 2, guardado en 0x555555558014
[537] contador = 2, guardado en 0x555555558014
[537] contador = 3, guardado en 0x555555558014
[536] contador = 3, guardado en 0x555555558014

Ahora los dos procesos guardan contador en la misma dirección, 0x555555558014, y aun así cada uno ve su propio valor. Dos datos distintos en la misma dirección solo son posibles porque esa dirección no es una posición física de la memoria RAM, sino una dirección dentro del espacio de direcciones de cada proceso: una vista de la memoria privada, que el núcleo y el hardware traducen, por separado para cada proceso, a posiciones físicas diferentes.

La primera ilusión: una memoria para cada uno

Cada proceso se ejecuta como si tuviera toda la memoria de la máquina para él solo, empezando en las mismas direcciones que cualquier otro. No puede ver, ni mucho menos modificar, la memoria de los demás procesos: para él, sencillamente, no existe. Esa es la base del aislamiento entre programas. Como se logra, con la memoria virtual y las tablas de páginas, es el tema del curso de Gestión de memoria; aquí nos alcanza con saber que cada proceso tiene su propio espacio de direcciones.

Observa también el orden de las últimas dos líneas: en esta ejecución el 537 terminó su tercer paso antes que el 536, al revés que en las líneas anteriores. Ninguno de los dos procesos decide cuándo se ejecuta; lo decide el sistema, y el orden puede cambiar de una vez a otra. Un programa nunca debe depender de que otro proceso vaya más rápido o más lento que él.

5. Una CPU para muchos procesos

La máquina de los ejemplos tiene 16 núcleos de CPU, de modo que los dos procesos podían ejecutarse literalmente al mismo tiempo, cada uno en un núcleo. Pero un sistema tiene casi siempre muchos más procesos que núcleos. ¿Como se reparten? Para verlo, este programa no hace otra cosa que usar la CPU, en un bucle que nunca termina:

giro.c
#define _POSIX_C_SOURCE 200809L

int main(void)
{
    volatile unsigned long vueltas = 0;

    for (;;)            /* nunca espera nada: solo usa la CPU */
        vueltas++;
}

El volatile impide que el compilador elimine el bucle por inútil. Lanzamos tres copias, y con taskset -c 0 las obligamos a usar todas el mismo núcleo, el número 0. Después de cinco segundos le preguntamos a ps en que núcleo está cada una (PSR) y que porcentaje de CPU usó (%CPU):

terminal · tres procesos, un núcleo
$ gcc -std=c17 -Wall -Wextra -o giro giro.c
$ taskset -c 0 ./giro & taskset -c 0 ./giro & taskset -c 0 ./giro &
$ sleep 5; ps -o pid,psr,pcpu,stat,comm -C giro
    PID PSR %CPU STAT COMMAND
    476   0 33.2 R+   giro
    477   0 33.2 R+   giro
    478   0 33.2 R+   giro
$ grep ctxt /proc/476/status
voluntary_ctxt_switches:	3
nonvoluntary_ctxt_switches:	419
$ pkill giro

Cada proceso cree estar ejecutándose sin parar, pero recibió exactamente un tercio de la CPU. Lo que ocurrió es que el núcleo los fue alternando: ejecutó uno durante unos milisegundos, lo detuvo, guardó su estado, cargó el estado de otro y lo dejó avanzar, y así sucesivamente. La línea nonvoluntary_ctxt_switches: 419 cuenta las veces que el proceso 476 fue detenido de esa forma, sin pedirlo, en esos cinco segundos: unas ochenta y cuatro veces por segundo.

Una línea de tiempo del núcleo 0 de la CPU. Los procesos 476, 477 y 478 se turnan: cada uno se ejecuta durante un intervalo corto, y entre un intervalo y otro el núcleo hace un cambio de contexto. Debajo, cada proceso tiene su propia fila, donde se ve que avanza solo en sus turnos y espera listo el resto del tiempo CPU 0 476 477 478 476 477 478 476 477 478 cambios de contexto 476 477 478 tiempo → ▬ en ejecución ┄ listo, esperando turno
El tiempo compartido: un solo núcleo, tres procesos que se turnan tan rápido que, a escala humana, parecen avanzar a la vez.

Esa operación de guardar el estado de un proceso y cargar el de otro se llama cambio de contexto, y tiene su tema propio, el 9. Decidir a quién le toca el turno, y durante cuánto tiempo, es la tarea del planificador, y es el tema del curso siguiente, planificación de la CPU.

La segunda ilusión: una CPU para cada uno

Igual que con la memoria, cada proceso se escribe como si tuviera un procesador propio, que ejecuta sus instrucciones una tras otra sin interrupciones. En realidad comparte los núcleos con todos los demás procesos, y puede ser detenido entre dos instrucciones cualesquiera. El sistema guarda y restaura su estado con tanto cuidado que el proceso no puede notarlo, salvo porque el reloj avanzó más de lo esperado.

6. Por que el sistema necesita procesos

Los primeros computadores ejecutaban un solo programa a la vez, de principio a fin, y el programa era dueño de la máquina entera. La idea de proceso aparece cuando se quiere tener varios programas en marcha al mismo tiempo, y resuelve, de una vez, varios problemas:

ProblemaCómo lo resuelve el proceso
Aprovechar la CPU. Un programa que espera el disco, la red o el teclado deja la CPU sin hacer nada durante millones de ciclos.Mientras un proceso espera, el sistema ejecuta otro. Es la multiprogramación: tener varios procesos cargados para que la CPU siempre tenga trabajo.
Atender a varios usuarios y tareas a la vez. Editar, compilar y escuchar música al mismo tiempo; o cien usuarios conectados a un servidor.El tiempo compartido alterna los procesos tan rápido que cada uno responde como si estuviera solo.
Proteger a unos programas de otros. Un error en un programa no debería romper a los demás, ni un programa debería poder leer los datos de otro.Cada proceso tiene su propio espacio de direcciones y sus propios permisos: el aislamiento. Si un proceso falla, el sistema lo termina a él solo.
Llevar la cuenta de los recursos. Saber quién usa cuánta memoria y cuánta CPU, y poder ponerle límites.El proceso es la unidad a la que se asignan recursos, se cobran tiempos y se aplican límites. Es lo que top muestra, fila por fila.
Controlar lo que se ejecuta. Poder detener, reanudar o terminar un programa concreto.Cada proceso tiene un PID con el que se lo puede nombrar, por ejemplo para enviarle una señal con kill.

Visto así, un sistema operativo es, en buena parte, un administrador de procesos: los crea, les reparte la CPU y la memoria, los protege unos de otros, les presta servicios a través de las llamadas al sistema y limpia lo que dejan al terminar. El proceso es la abstracción que hace todo eso posible: oculta los detalles de la máquina real y le ofrece a cada programa una máquina más simple, propia y protegida.

7. Ver los procesos del sistema

En el curso de terminal usaste ps y top para ver procesos. Ahora conviene mirarlos con otros ojos. Si mientras se ejecutan dos copias de proceso pedimos a ps los procesos de esta terminal, con su PID, el PID de su padre y su estado:

terminal · los procesos de esta sesión
$ ./proceso > /dev/null & ./proceso > /dev/null &
$ ps -o pid,ppid,stat,comm
    PID    PPID STAT COMMAND
    331     330 Ss+  bash
    468     331 S+   proceso
    469     331 S+   proceso
    471     331 R+   ps

Aparecen cuatro procesos: la shell, bash, con PID 331; las dos copias de proceso; y el propio ps, que también es un proceso mientras se ejecuta. Los tres últimos tienen como padre a 331: la shell los creó a todos. La columna STAT muestra el estado: S es sleeping, dormido, porque los dos proceso pasan casi todo el tiempo dentro de sleep(1); R es running, en ejecución, porque ps estaba trabajando cuando se miró a sí mismo. Los estados son el tema del bloque 2.

Si se pide la lista completa, con ps -e, aparecen todos los procesos del sistema, y pstree los dibuja como lo que son, un árbol en el que cada proceso cuelga de su padre:

terminal · todos los procesos del sistema (salida recortada)
$ ps -e --no-headers | wc -l
27
$ ps -p 1 -o pid,ppid,comm
    PID    PPID COMMAND
      1       0 systemd
$ pstree -p
systemd(1)-+-cron(151)
           |-dbus-daemon(152)
           |-login(332)---bash(402)
           |-rsyslogd(184)-+-{rsyslogd}(217)
           |               |-{rsyslogd}(218)
           |               `-{rsyslogd}(219)
           |-systemd-journal(44)
           |-systemd-logind(169)
           |-systemd-resolve(65)
           |-systemd-udevd(88)
           …

Esta máquina, un ubuntu mínimo dentro de WSL, tiene 27 procesos; un escritorio con un navegador abierto tiene fácilmente varios cientos. En la raíz del árbol está el proceso 1, systemd, el primero que crea el núcleo al arrancar y el antepasado de todos los demás. Los nombres entre llaves, como {rsyslogd}, no son procesos sino hilos dentro del proceso rsyslogd: el servicio de registro trabaja con cuatro hilos que comparten su memoria. Los hilos son el tema del bloque 7.

8. El directorio /proc

¿De dónde saca ps esa información? En linux, el núcleo publica los datos de cada proceso como si fueran archivos, en un sistema de archivos virtual montado en /proc. Cada proceso tiene un directorio con el nombre de su PID. Para mirarlo con calma, compilamos una versión de proceso.c con el bucle alargado a 30 pasos, proceso30, y la dejamos corriendo en segundo plano:

terminal · un directorio por proceso
$ ./proceso30 > /dev/null &
[1] 440
$ ls /proc
1    221  428  acpi       dma            kallsyms      meminfo       scsi           tty
171  224  429  buddyinfo  driver         kcore         misc          self           uptime
172  233  430  bus        dynamic_debug  key-users     modules       slabinfo       version
173  294  431  cgroups    execdomains    keys          mounts        softirqs       vmallocinfo
177  332  440  cmdline    fb             kmsg          mpt           stat           vmstat
…
$ ls /proc/440
arch_status      cpuset   ksm_merging_pages  mountinfo   oom_score_adj  sessionid      syscall
attr             cwd      ksm_stat           mounts      pagemap        setgroups      task
auxv             environ  latency            mountstats  patch_state    smaps          timens_offsets
cgroup           exe      limits             net         personality    smaps_rollup   timers
clear_refs       fd       loginuid           ns          projid_map     stack          timerslack_ns
cmdline          fdinfo   map_files          numa_maps   root           stat           uid_map
comm             gid_map  maps               oom_adj     sched          statm          wchan
coredump_filter  io       mem                oom_score   schedstat      status
$ grep -E '^(Name|State|Pid|PPid|Threads|VmRSS)' /proc/440/status
Name:	proceso30
State:	S (sleeping)
Pid:	440
PPid:	427
VmRSS:	    1792 kB
Threads:	1
$ ls -l /proc/440/exe /proc/440/cwd
lrwxrwxrwx 1 ana ana 0 oct 15 23:06 /proc/440/cwd -> /home/ana/procesos
lrwxrwxrwx 1 ana ana 0 oct 15 23:06 /proc/440/exe -> /home/ana/procesos/proceso30
$ grep -E '^(Name|Pid|PPid)' /proc/self/status
Name:	grep
Pid:	448
PPid:	427

En /proc conviven los directorios numerados, uno por proceso, con archivos de información general del sistema, como cpuinfo, meminfo o uptime. Ninguno existe en el disco: cada vez que se los lee, el núcleo genera su contenido a partir de lo que sabe en ese instante. Dentro del directorio de un proceso, status resume su nombre, su estado, su PID, el de su padre, la memoria que ocupa y su número de hilos; cwd apunta a su directorio de trabajo; fd contiene sus archivos abiertos; maps, su mapa de memoria. Y exe es un enlace al archivo del programa que el proceso está ejecutando: el puente entre el proceso y su programa. Si hubiera dos copias de proceso30 en marcha, las dos tendrían su exe apuntando al mismo archivo.

La última orden usa un nombre especial, /proc/self, que para cada proceso que lo lee apunta a su propio directorio. Por eso el resultado no habla de la shell sino de grep, el proceso 448, que es quien abrió el archivo. Un programa en C puede así consultar sus propios datos abriendo /proc/self/status como cualquier archivo. Usaremos /proc a lo largo de todo el curso para mirar dentro de los procesos que creemos.

9. Cuando programa y proceso no coinciden

En el ejemplo del principio, cada proceso ejecutaba un programa y cada ejecución creaba un proceso. Esa es la situación más común, pero no la única, y el curso recorre todas las demás:

SituaciónEjemploTemas
Un programa, muchos procesosDos terminales abiertas son dos procesos bash del mismo archivo, cada uno con sus variables y su historial.1, 11
Un proceso que crea otro igual a sí mismofork duplica al proceso que la llama: el hijo empieza con una copia de la memoria del padre.11–15
Un proceso que cambia de programaexec reemplaza el programa de un proceso por otro, sin cambiar su PID. Así ejecuta la shell cada orden.16–19
Un proceso con varios hilosEl rsyslogd de antes: un solo proceso, con una sola memoria, y cuatro hilos de ejecución dentro.28–36
Una aplicación hecha de muchos procesosUn navegador moderno separa cada sitio en procesos distintos, para que un error en una pestaña no afecte a las demás.39
Un proceso terminado que todavía existeUn hijo que terminó, pero cuyo padre aún no recogió su código de salida: un zombi.24, 25

Por eso conviene no confundir nunca las dos palabras. Una aplicación, como el navegador o un servidor web, es lo que el usuario ve; un programa es un archivo; y un proceso es lo que el sistema operativo administra.

10. Cómo trabajaremos

Este curso se apoya en Programación en C para sistemas operativos, y sigue sus mismas costumbres. Todos los ejemplos están probados en linux, con gcc 15 y glibc 2.43, y sirven igual en una máquina real, una máquina virtual o WSL. Además de las herramientas de desarrollo, usaremos algunas para observar procesos:

terminal · las herramientas del curso, en debian y ubuntu
$ sudo apt install build-essential gdb strace procps psmisc
# procps trae ps, top y pgrep; psmisc trae pstree y killall

Y cuatro reglas que valen para todos los programas del curso:

  1. Compilar con el estándar y las advertencias: gcc -std=c17 -Wall -Wextra, y -g -fsanitize=address,undefined mientras se desarrolla. gcc
  2. Pedir la interfaz POSIX: casi todas las funciones del curso, como getpid, fork o waitpid, son de POSIX y no del estándar de C. Por eso cada programa empieza con #define _POSIX_C_SOURCE 200809L, antes de cualquier #include, como aprendiste en el curso de C. POSIX
  3. Comprobar cada llamada al sistema: fork, exec, wait y pthread_create pueden fallar, y un programa que no lo comprueba se comporta de forma muy difícil de entender. Los ejemplos mínimos del principio omiten algunas comprobaciones para que se lea la idea; a partir del bloque 3 las haremos siempre. errores
  4. Observar desde afuera: cada programa que cree procesos o hilos se mira con ps, pstree, /proc y strace mientras se ejecuta. Ver lo que el sistema hizo es la mejor forma de entender por que lo hizo. observar
Los números cambiarán

Los PID, las direcciones de memoria y el orden en que se intercalan las líneas de procesos simultáneos son distintos en cada ejecución y en cada máquina. Cuando repitas los ejemplos no obtendrás exactamente las mismas salidas, y eso es parte de lo que se aprende: un programa con varios procesos o hilos no se ejecuta siempre en el mismo orden.

11. El mapa del curso

Los 43 temas avanzan desde la idea de proceso hasta la decisión de usar procesos o hilos en un programa real. Cada bloque responde una pregunta:

BloqueTemasLa pregunta que responde
1. El concepto de proceso1–5¿Que es un proceso, que tiene en su memoria y que sabe el núcleo de él?
2. Estados y cambio de contexto6–10¿Por que estados pasa un proceso, y como se reparten la CPU?
3. Crear procesos con fork11–15¿Como nace un proceso nuevo, y que recibe de su padre?
4. Ejecutar programas con exec16–19¿Como pasa un proceso a ejecutar otro programa?
5. Esperar y terminar20–23¿Como termina un proceso, y como se entera su padre?
6. Zombis, huérfanos y demonios24–27¿Que pasa cuando padre e hijo no terminan en el orden esperado?
7. Hilos y la biblioteca pthreads28–33¿Como se divide un proceso en varios hilos que comparten la memoria?
8. Hilos en profundidad34–36¿Que problemas trae compartir la memoria, y que datos son de cada hilo?
9. Procesos frente a hilos en la práctica37–40¿Cuándo conviene cada modelo en servidores, navegadores y videojuegos?
10. Integración y cierre41–43¿Como se usa todo junto en una shell y en un programa paralelo?

Los bloques 1 y 2 son sobre todo conceptuales, aunque con muchos experimentos; desde el bloque 3 en adelante, cada tema se construye alrededor de programas en C que crean y controlan procesos e hilos.

12. Resumen

13. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Que diferencia hay entre un programa y un proceso?

Un programa es un archivo con instrucciones y datos iniciales, guardado en el disco; no cambia ni hace nada por sí mismo. Un proceso es una ejecución concreta de ese programa: tiene su propia memoria, su estado de la CPU, sus recursos y un PID, cambia a cada instante y deja de existir cuando termina.

2. Al ejecutar dos copias de proceso a la vez, ninguna llegó a contar hasta 6. ¿Por que, si contador es una variable global?

Porque cada proceso tiene su propia memoria. La variable es global dentro del programa, pero cada proceso tiene su propia copia de ella, en su propio espacio de direcciones, y lo que uno modifica no afecta al otro.

3. Con setarch -R, los dos procesos mostraron la misma dirección para contador pero valores distintos. ¿Como es posible?

Porque esas direcciones no son posiciones físicas de la RAM, sino direcciones dentro del espacio de direcciones de cada proceso. El núcleo y el hardware traducen la misma dirección a posiciones físicas distintas para cada proceso.

4. Nombra cuatro cosas que el sistema le agrega a un programa al convertirlo en proceso.

Por ejemplo: un espacio de memoria propio, con montículo y pila; el estado de los registros de la CPU, incluido el contador de programa; recursos como los archivos abiertos, el directorio de trabajo y el usuario; un PID, un padre y un estado.

5. ¿Que es el PCB, y por que se dice que para el núcleo un proceso «es» su PCB?

El bloque de control de proceso es la estructura donde el núcleo guarda toda la información de un proceso: su PID, su estado, sus registros, su memoria, sus archivos abiertos. Con esa información el núcleo puede detener al proceso y reanudarlo después exactamente donde estaba, así que todo lo que el sistema sabe y hace con un proceso pasa por ella.

6. Tres procesos giro en un mismo núcleo recibieron un 33 % de CPU cada uno. ¿Que hizo el sistema para repartirla?

Los alternó: ejecutó cada uno durante unos milisegundos, lo detuvo, guardó su estado y cargó el de otro. Esa operación es el cambio de contexto, y nonvoluntary_ctxt_switches en /proc/PID/status cuenta las veces que un proceso fue detenido así sin pedirlo.

7. ¿Que es la multiprogramación, y que problema resuelve?

Es tener varios procesos cargados en memoria para que, cuando uno espera una operación lenta como leer el disco o recibir datos de la red, la CPU pueda ejecutar otro en lugar de quedar sin trabajo.

8. En la salida de ps, ¿por que los dos proceso aparecían con estado S y el propio ps con R?

Porque los dos proceso pasaban casi todo el tiempo dentro de sleep(1), dormidos esperando que pasara el segundo, mientras que ps estaba ejecutándose en el momento en que leyó su propio estado.

9. ¿Que es /proc/PID/exe, y que muestra sobre la relación entre programa y proceso?

Es un enlace, generado por el núcleo, al archivo del programa que el proceso está ejecutando. Muestra que el proceso y el programa son cosas distintas pero relacionadas: varios procesos pueden tener su exe apuntando al mismo archivo.

10. En pstree aparecían nombres entre llaves, como {rsyslogd}. ¿Que son?

Son hilos, no procesos: líneas de ejecución dentro del proceso rsyslogd que comparten su memoria. El servicio es un único proceso con cuatro hilos.

Para practicar

  1. Compila proceso.c y ejecuta tres copias a la vez con ./proceso & ./proceso & ./proceso & wait. Comprueba que los tres tienen el mismo padre y que ese padre es el valor de echo $$.
  2. Cambia el bucle de proceso.c para que dure 30 pasos. Mientras se ejecutan dos copias, compara desde otra terminal ls -l /proc/PID/exe de cada una y el valor de VmRSS en /proc/PID/status.
  3. Repite el experimento de giro con dos copias en el núcleo 0, y después con cuatro. ¿Que porcentaje recibe cada una? ¿Y si lanzas dos copias sin taskset? Observa la columna PSR.
  4. Ejecuta top y busca los procesos giro mientras se ejecutan. Pulsa 1 para ver cada núcleo por separado. Termínalos al final con pkill giro.
  5. Cuenta los procesos de tu sistema con ps -e --no-headers | wc -l y dibuja su árbol con pstree -p. Encuentra el camino desde el proceso 1 hasta la shell en la que estás trabajando.
  6. Escribe un programa que abra /proc/self/status con fopen y muestre solo las líneas que empiezan con Name, Pid, PPid y State. Ejecútalo dos veces y compara los resultados.