Qué pasa al ejecutar un programa: proceso, RAM y API

David TorresInformática y FPLeave a Comment

Del archivo a la respuesta: un programa se ejecuta como proceso, guarda sus datos en la RAM y contesta a otros en JSON

Del archivo a la respuesta: tres preguntas

En el artículo anterior abrías una web: tu navegador le pedía una página a un servidor y el servidor contestaba. Pero ¿qué es ese servidor? No es una máquina mágica: es un programa en marcha en un ordenador, esperando a que alguien le pregunte.

Para entenderlo de verdad hay que contestar tres preguntas, en este orden:

  1. ¿Qué está en marcha? Un programa es un archivo; cuando lo ejecutas, el sistema crea un proceso.
  2. ¿Dónde guarda lo que sabe? Mientras funciona, en la RAM. Lo que tiene que sobrevivir, en el disco.
  3. ¿Cómo habla con otros? A través de una API: le llega una petición HTTP y contesta con datos, normalmente en JSON.
Mapa de lo que pasa al ejecutar api.py: el archivo en el disco, el proceso con el PID 12233 y su contador en la RAM, el puerto 8000 que escucha y la respuesta en JSON que recibe curl, con lo que se ve cuando falla cada paso

Las tres se ven con el mismo ejemplo, un servidor de veinticinco líneas en Python. Este es entero:

# api.py: una API mínima. Contesta en JSON y cuenta las visitas en memoria.
import json
import os
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer

puerto = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
visitas = 0


class Api(BaseHTTPRequestHandler):
    def do_GET(self):
        global visitas
        visitas += 1
        datos = {"pid": os.getpid(), "puerto": puerto, "visitas": visitas}
        cuerpo = json.dumps(datos).encode()
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(cuerpo)


servidor = HTTPServer(("127.0.0.1", puerto), Api)
print(f"Escuchando en el puerto {puerto} con el PID {os.getpid()}")
servidor.serve_forever()

No hace falta entender cada línea. Lo que importa: cada vez que alguien le pide algo, suma uno a visitas y contesta con tres datos, su PID, su puerto y cuántas visitas lleva. Solo usa la biblioteca estándar de Python, así que no hay nada que instalar aparte de Python. Los resultados de este artículo salen de un Mac con Python 3.13; en cada paso se indica qué cambia en Linux y en Windows.

1 · Programa y proceso

Guárdalo como api.py. Ahora mismo es un programa: un archivo de unos 750 bytes en el disco. No hace nada. Puedes copiarlo, borrarlo o mandarlo por correo, y seguirá sin hacer nada.

Ejecútalo:

$ python3 api.py
Escuchando en el puerto 8000 con el PID 12233

Ahora sí hay algo en marcha: un proceso. El sistema operativo ha leído el archivo, le ha reservado memoria y le ha dado un número, el PID (process identifier), que lo identifica mientras viva. Con ese número puedes preguntarle al sistema por él desde otra terminal:

$ ps -o pid,command -p 12233
  PID COMMAND
12233 …/Python api.py

La ruta completa del intérprete es larga y aquí va recortada. En Linux, la misma orden funciona igual. En Windows, el equivalente es tasklist /FI "PID eq 12233", y en lugar de python3 se escribe python.

Un programa es la receta; un proceso es alguien cocinándola. La receta está en el disco aunque nadie cocine. El proceso existe solo mientras se cocina.

El mismo programa, dos procesos

Abre una tercera terminal y lanza el mismo archivo otra vez, en el puerto 8001:

$ python3 api.py 8001
Escuchando en el puerto 8001 con el PID 12235

Un programa, dos procesos, cada uno con su PID y su memoria. No comparten nada: lo comprobarás en el paso 2. Es lo mismo que hace tu navegador cuando abres varias pestañas, y por eso en el administrador de tareas ves muchos procesos con el mismo nombre.

Ahora intenta lanzar un tercero en el 8000, que ya está ocupado:

$ python3 api.py
Traceback (most recent call last):
  …
OSError: [Errno 48] Address already in use

Dos procesos no pueden escuchar en el mismo puerto. El error no dice que tu programa esté mal: dice que otro proceso ya tiene ese puerto, y casi siempre es uno que dejaste en marcha en otra terminal. En Linux el número es 98, no 48; el texto es el mismo.

Un servidor es un proceso que espera

api.py no termina: se queda esperando. Eso es un servidor, un proceso que escucha en un puerto a que alguien le pregunte. Puedes ver quién escucha en el 8000:

$ lsof -nP -i :8000
COMMAND   PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
Python  12233 …      3u  IPv4  …           0t0  TCP 127.0.0.1:8000 (LISTEN)

El PID es el mismo que imprimió al arrancar. En Linux, lo habitual es ss -ltnp; en Windows, netstat -ano | findstr :8000, que da el PID en la última columna.

El servidor de saseformacion.es del artículo anterior es exactamente esto, a otra escala: un proceso en una máquina que escucha en el puerto 443.

2 · Dónde guarda el proceso lo que sabe

Reinícialo y cuenta

Pregúntale a tu servidor un par de veces con curl:

$ curl localhost:8000
{"pid": 12233, "puerto": 8000, "visitas": 1}
$ curl localhost:8000
{"pid": 12233, "puerto": 8000, "visitas": 2}
$ curl localhost:8001
{"pid": 12235, "puerto": 8001, "visitas": 1}

localhost es tu propio equipo. Y fíjate en el tercero: el proceso del 8001 lleva 1 visita, no 3. Cada proceso tiene su propia memoria, y el contador de uno no es el del otro.

Ahora para el del 8000 con Ctrl + C en su terminal (verás un KeyboardInterrupt: es normal, es como se detiene) y vuelve a lanzarlo:

$ python3 api.py
Escuchando en el puerto 8000 con el PID 12252
$ curl localhost:8000
{"pid": 12252, "puerto": 8000, "visitas": 1}

Otro PID, y la cuenta ha vuelto a 1. La variable visitas vivía en la RAM, la memoria de trabajo del ordenador. Cuando el proceso termina, el sistema recupera esa memoria y lo que había en ella desaparece. Lo mismo pasa si se va la luz.

Lo que tiene que sobrevivir va al disco. Bastan tres líneas para que api.py guarde la cuenta en un archivo:

from pathlib import Path

archivo = Path("visitas.txt")
visitas = int(archivo.read_text()) if archivo.exists() else 0

Esas líneas sustituyen a visitas = 0, con el import arriba junto a los demás, y dentro de do_GET, justo después de visitas += 1, se añade:

        archivo.write_text(str(visitas))

Reinicia y prueba: ahora el número sigue donde lo dejaste, aunque el PID cambie.

$ curl localhost:8000
{"pid": 12277, "puerto": 8000, "visitas": 2}
  (Ctrl + C y python3 api.py)
$ curl localhost:8000
{"pid": 12286, "puerto": 8000, "visitas": 3}

Esa es la diferencia que importa entre la RAM y el disco. La RAM es rápida y se vacía; el disco es más lento y conserva. Por eso tu programa trabaja en la RAM y guarda en el disco. Y por eso una base de datos, que es lo que se usa en serio en lugar de visitas.txt, es también un proceso que escribe en disco.

Caché: una copia más cerca

Entre la RAM y el procesador hay otro escalón: la caché. Es una memoria pequeña y muy rápida dentro del propio procesador, donde se guarda una copia de lo que se está usando ahora para no ir a buscarlo a la RAM cada vez.

No hace falta exagerar cifras para ver la escala: basta con preguntarle a tu equipo. En el Mac de este artículo (macOS, con sysctl y diskutil):

NivelTamaño en este equipoSe vacía al apagar
Caché L1 (por núcleo)128 KBSí
Caché L216 MBSí
RAM24 GBSí
Disco (SSD)494 GBNo

De arriba abajo se pasa de kilobytes a cientos de gigabytes, y cada nivel es más lento que el de arriba. Arriba, poco y a mano; abajo, mucho y más lejos. En Linux, lscpu muestra las cachés y free -h la RAM; en Windows, el Administrador de tareas, en Rendimiento.

Lo que conviene llevarse es la idea, porque se repite en todas partes: una caché es una copia de algo, guardada más cerca para no ir a buscarlo lejos cada vez. Los 304 que veías en la pestaña Red del navegador eran la caché del navegador. La respuesta del DNS que se guardaba un rato, también. Si la borras, no pierdes el original: la próxima vez se vuelve a buscar, solo que tarda más.

3 · Hablar con otro proceso: API y JSON

Tu curl también es un proceso. Cuando escribes curl localhost:8000, un proceso le hace una petición HTTP a otro, y el segundo le contesta. La puerta por la que un programa deja que otros le pidan cosas, con unas reglas fijas de qué se pide y qué se devuelve, es una API (application programming interface).

La de api.py es mínima: pides / y te devuelve tres datos. Con -i, curl enseña también la cabecera de la respuesta:

$ curl -i localhost:8000
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.13.4
Date: Sat, 31 Oct 2026 16:35:47 GMT
Content-Type: application/json

{"pid": 12252, "puerto": 8000, "visitas": 2}

Es la misma conversación del artículo anterior: un código, 200, unas cabeceras y un cuerpo. Solo cambia una cosa, el Content-Type. Antes era text/html, una página para que la pinte un navegador. Ahora es application/json, datos para que los use otro programa.

La misma web, otra respuesta

No hace falta un ejemplo de juguete para verlo. El blog donde lees esto es un WordPress, y WordPress tiene una API. Pide la portada y te devuelve HTML; pide esta otra ruta y te devuelve la última entrada en JSON:

$ curl -s "https://saseformacion.es/wp-json/wp/v2/posts?per_page=1&_fields=id,title,link"
[{"id":5944,"link":"https:\/\/saseformacion.es\/php2shell-que-hacer-cve-critico\/","title":{"rendered":"PHP2Shell: qu\u00e9 hacer cuando sale un CVE cr\u00edtico en PHP"}}]

Mismo servidor, mismo HTTP. La ruta decide si la respuesta es una página para personas o datos para programas. Así es como una app de móvil o una web moderna se habla con su servidor: pide JSON y lo pinta ella. En Windows, recuerda escribir curl.exe.

El resultado cambia con cada entrada nueva del blog. Cuando lo pruebes, verás la última que haya.

Cómo se lee un JSON

JSON (JavaScript Object Notation) es un formato de texto para datos, no un lenguaje de programación: no calcula nada, solo ordena. Tiene muy pocas piezas:

  • { } es un objeto: pares de clave: valor. {"id": 5944}.
  • [ ] es una lista: varios valores en orden. La respuesta del blog es una lista con una sola entrada.
  • Los valores son texto entre comillas dobles, números, true, false, null, u otros objetos y listas dentro. title es un objeto dentro del objeto.

Las \/ y los \u00e9 no son errores: son la / y la é escritas con escapes, que JSON permite y WordPress usa. Para leerlo cómodo, pásaselo a Python:

$ curl -s "https://saseformacion.es/wp-json/wp/v2/posts?per_page=1&_fields=id,title,link" | python3 -m json.tool --no-ensure-ascii
[
    {
        "id": 5944,
        "link": "https://saseformacion.es/php2shell-que-hacer-cve-critico/",
        "title": {
            "rendered": "PHP2Shell: qué hacer cuando sale un CVE crítico en PHP"
        }
    }
]

Si abres la misma dirección en Firefox, también te lo enseña ordenado.

«La API no responde»: en qué paso mirar

Con los tres pasos en fila, «no responde» deja de ser una sola cosa:

SíntomaQué pasaCómo lo compruebas
curl: (7) … Couldn't connect to serverNo hay ningún proceso escuchando en ese puertops o lsof: ¿está en marcha? ¿en ese puerto?
Address already in use al arrancarOtro proceso ya tiene el puertolsof -nP -i :8000 te dice cuál
Contesta, pero los datos han vuelto a ceroEl proceso se ha reiniciado y lo tenía en RAM¿Ha cambiado el PID? Lo que importa, al disco
Contesta con un 4xx o 5xxEl proceso está vivo y te ha contestadoLee el código, como en el artículo anterior

El primero, provocado a propósito, con el servidor parado:

$ curl localhost:8000
curl: (7) Failed to connect to localhost port 8000 after 0 ms: Couldn't connect to server

Como el Timeout del artículo anterior, aquí no hay código HTTP: no ha habido conversación porque al otro lado no había nadie. La diferencia es que el timeout espera a una máquina que no contesta, y este error llega al instante porque la máquina contesta, pero nadie escucha en ese puerto.

Errores frecuentes

  • «He cerrado la ventana, así que el programa ya no está», o al revés, «he borrado el archivo y el servidor sigue funcionando». El archivo y el proceso son dos cosas: el proceso sigue mientras nadie lo detenga, aunque el archivo cambie o desaparezca. Por eso, después de cambiar api.py, hay que reiniciarlo para ver el cambio.
  • «Address already in use: mi código está mal». Tu código está bien: hay otro proceso en ese puerto. Búscalo con lsof o mira tus otras terminales.
  • «Tengo 24 GB de RAM, me caben más archivos». La RAM no guarda archivos: es memoria de trabajo y se vacía. Lo que cabe en tu equipo lo dice el disco.
  • «Borrar la caché borra mis datos». La caché es una copia: al borrarla se pierde la copia, no el original. Las cookies y las contraseñas guardadas del navegador son otra cosa y se borran aparte.
  • «JSON es un lenguaje de programación» o «una API es una página web». JSON es un formato de datos. Una API puede ir por HTTP, como la de una web, pero contesta datos para un programa, no una página para una persona.

Ejercicio: tu primera API en cinco minutos

Necesitas Python 3 y dos terminales. En Windows, python en lugar de python3 y curl.exe en lugar de curl.

  1. Del archivo al proceso. Guarda api.py y lánzalo en la primera terminal. Apunta el PID que imprime.
  2. Pregúntale. En la segunda, curl localhost:8000 tres veces. ¿Qué número de visitas te da la tercera?
  3. Compruébalo como proceso. ps -o pid,command -p <tu PID> (en Windows, tasklist /FI "PID eq <tu PID>"). ¿Coincide con el que sale en el JSON?
  4. Dos procesos. Lanza una segunda copia con python3 api.py 8001 en otra terminal y pregúntale. ¿Cuántas visitas tiene? ¿Por qué no las mismas que el 8000?
  5. Reinicia. Para el del 8000 con Ctrl + C, lánzalo otra vez y pregúntale. Apunta el PID y las visitas.
  6. Rómpelo. Con el 8000 parado, curl localhost:8000. ¿Qué error da? ¿Hay código HTTP?
  7. Que sobreviva. Haz el cambio del archivo visitas.txt, reinicia dos veces y comprueba que la cuenta sigue.

Si en el paso 5 sabes explicar por qué la cuenta vuelve a 1 y en el 7 no, ya tienes el mapa: el archivo es el programa, lo que corre es el proceso, lo que sabe vive en la RAM hasta que lo guarda en el disco, y lo cuenta a otros por su API.

Por dónde seguir

Cada paso tiene su vídeo de un minuto:

  • Proceso vs programa — el mismo archivo, dos PID.
  • RAM, disco y caché — por qué la cuenta vuelve a 1.
  • API: petición, respuesta y JSON — la misma web, una página o datos.

Viene de Qué pasa al abrir una web: IP, DNS, HTTP y HTML, en orden, que te llevaba hasta la puerta del servidor; este artículo entra dentro.

Y si sistemas o programación se te están haciendo cuesta arriba, en SASE Formación damos clases de informática y programación, presenciales y online. La primera clase de prueba es gratuita, y en informática las clases son individuales. Escríbenos y nos cuentas por dónde vas: saseformacion.es.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *