Volver a Procesos e Hilos Bloque 9 - Procesos frente a hilos en la práctica - Tema 39 de 43

Tema 39

Navegadores

Los navegadores modernos son aplicaciones complejas que combinan procesos e hilos de formas sofisticadas. Usan procesos para aislar pestañas y sitios, e hilos para paralelizar el renderizado dentro de cada pestaña. En este tema verás la arquitectura multiproceso de Chrome y Firefox, el aislamiento de sitios, y los hilos dentro de cada pestaña.

1. El problema de los navegadores

Un navegador es una aplicación compleja que debe ejecutar código no confiable (JavaScript, HTML, CSS) de sitios web arbitrarios, mientras mantiene la seguridad y la estabilidad del sistema. El problema es: ¿cómo aislar los sitios web entre sí y del sistema operativo?

Los navegadores antiguos (como Internet Explorer 6 o Firefox 2) usaban un solo proceso para todo: todas las pestañas, todos los sitios, y la interfaz de usuario compartían el mismo espacio de memoria. Esto tenía graves problemas:

Los navegadores modernos resolvieron estos problemas usando procesos para aislar pestañas y sitios, e hilos para paralelizar el trabajo dentro de cada pestaña.

El navegador es el ejemplo supremo de procesos + hilos

Los navegadores modernos combinan procesos (para aislamiento) e hilos (para paralelismo) de formas sofisticadas. Son el ejemplo más completo y más complejo de los conceptos que hemos estudiado.

2. La arquitectura de un navegador

Un navegador moderno (como Chrome o Firefox) tiene una arquitectura multiproceso con varios tipos de procesos:

ProcesoFunciónCantidad
Browser processInterfaz de usuario, gestión de pestañas, red, disco, seguridad.1
Renderer processRenderizado de una pestaña o sitio. Ejecuta HTML, CSS, JavaScript.1 por pestaña o sitio
GPU processRenderizado de gráficos (composición, WebGL).1
Plugin processEjecución de plugins (Flash, PDF, etc.).1 por plugin
Utility processTareas auxiliares (decodificación de audio/video, etc.).Variable

En Chrome, se puede ver esta arquitectura con el administrador de tareas (Shift+Esc) o con chrome://process-internals.

Arquitectura multiproceso de un navegador. El browser process gestiona la UI y los renderer processes. Cada renderer process renderiza una pestaña o sitio. Browser Process (PID 1000) - UI, red, seguridad Renderer (PID 1001) Pestaña 1: sitio A Hilos: main, compositor, worker Renderer (PID 1002) Pestaña 2: sitio B Hilos: main, compositor, worker Renderer (PID 1003) Pestaña 3: sitio A Hilos: main, compositor, worker GPU Process (PID 1004) Renderizado de gráficos, WebGL Plugin Process (PID 1005) Flash, PDF, etc. IPC (pipes, sockets, shared memory) entre procesos Cada renderer process tiene múltiples hilos internos Hilos dentro de un renderer process: Main thread (HTML parsing, JS execution) - Compositor thread (rendering) - Worker threads (Web Workers, Service Workers) IO thread (network, disk) - Raster threads (painting)
Arquitectura multiproceso de un navegador. El browser process gestiona la UI. Cada pestaña tiene su propio renderer process con múltiples hilos.

3. Por qué los navegadores usan procesos

Los navegadores usan procesos para las pestañas por las mismas razones que vimos en el tema 37:

La adopción de la arquitectura multiproceso fue un cambio importante. Chrome fue el primer navegador en adoptarla (2008), y Firefox la adoptó después (2017, con el proyecto Electrolysis).

Antes de la arquitectura multiproceso, los navegadores usaban un solo proceso para todo. Un error en cualquier parte (un plugin, una pestaña, un sitio) colgaba todo el navegador. Esto era inaceptable para los usuarios.

Chrome fue el pionero

Chrome (2008) fue el primer navegador en usar una arquitectura multiproceso. Esto fue una de las razones de su éxito: era más estable y más seguro que los competidores. Hoy, todos los navegadores modernos usan procesos.

4. El aislamiento de sitios

El aislamiento de sitios (site isolation) es una técnica que va más allá del aislamiento de pestañas: cada sitio (origen) se ejecuta en un proceso separado, incluso si está en la misma pestaña.

Esto protege contra ataques como Spectre, que permiten leer la memoria del proceso. Con site isolation, un sitio malicioso no puede leer la memoria de otros sitios, porque están en procesos diferentes.

Chrome implementa site isolation desde 2018. Cada origen (protocolo + dominio + puerto) tiene su propio proceso de renderizado. Si una pestaña tiene iframes de diferentes orígenes, cada iframe se renderiza en un proceso diferente.

Sin site isolationCon site isolation
Una pestaña = un procesoUn origen = un proceso
Los iframes comparten procesoCada iframe tiene su propio proceso
Vulnerable a SpectreProtegido contra Spectre
Menos memoriaMás memoria (más procesos)

El costo de site isolation es el consumo de memoria: cada proceso adicional usa memoria. Pero la seguridad compensa el costo.

Firefox implementa un modelo similar con "Fission" (site isolation), que se activó por omisión en Firefox 79 (2020).

5. Los hilos dentro de cada pestaña

Dentro de cada renderer process (pestaña), hay múltiples hilos que paralelizan el trabajo:

HiloFunción
Main threadParsea HTML, ejecuta JavaScript, construye el DOM. Es el hilo principal y el único que puede modificar el DOM.
Compositor threadCompone las capas del renderizado. Permite scroll suave sin bloquear el main thread.
Raster threadsPintan las capas en bitmaps. Se ejecutan en paralelo para acelerar el renderizado.
Worker threadsEjecutan Web Workers (JavaScript en segundo plano) y Service Workers.
IO threadManeja la comunicación con el browser process (IPC, red, disco).

Esta arquitectura de hilos permite que el navegador sea responsivo: el main thread puede ejecutar JavaScript mientras el compositor thread hace scroll suave.

El problema es que JavaScript es single-threaded: todo el código JS se ejecuta en el main thread. Si un script bloquea el main thread, la página se congela. Los Web Workers permiten ejecutar JS en hilos separados, pero no pueden acceder al DOM.

El main thread es el cuello de botella

Todo el JavaScript se ejecuta en el main thread. Si un script tarda mucho, la página se congela. La solución es usar Web Workers para tareas pesadas, y mantener el main thread libre para la UI.

6. El proceso del navegador

El browser process es el proceso principal del navegador. Gestiona:

El browser process es el único proceso que tiene acceso a la UI y a los recursos del sistema. Los renderer processes no tienen acceso directo a la red ni al disco: piden al browser process que haga las operaciones.

Esto es importante para la seguridad: un sitio malicioso (en un renderer process) no puede acceder a los archivos del sistema ni a la red directamente. Solo puede pedirle al browser process que haga operaciones, y el browser process valida cada petición.

7. El proceso de renderizado

El renderer process es el proceso que renderiza una pestaña o sitio. Ejecuta el código HTML, CSS y JavaScript del sitio. Es el proceso que más memoria consume y el que más riesgo de seguridad tiene (ejecuta código no confiable).

Cada renderer process tiene un sandbox: un conjunto de restricciones que limitan lo que puede hacer. El sandbox impide que el renderer process acceda a archivos del sistema, a la red, o a otros procesos. Solo puede comunicarse con el browser process a través de IPC.

El sandbox es la razón por la que los renderer processes son seguros: aunque un sitio malicioso explote una vulnerabilidad en el renderer, no puede acceder al sistema porque el sandbox lo impide.

Sin sandboxCon sandbox
Acceso a archivos del sistemaSin acceso (solo a través del browser process)
Acceso a la redSin acceso (solo a través del browser process)
Acceso a otros procesosSin acceso
Ejecución de código nativoRestringida

El sandbox se implementa con diferentes mecanismos según el sistema operativo: seccomp-bpf en Linux, job objects en Windows, seatbelt en macOS.

8. El proceso de GPU

El GPU process es el proceso que gestiona el acceso a la GPU. El renderizado de gráficos (composición de capas, WebGL, decodificación de video) se hace en la GPU para acelerar el rendimiento.

El GPU process se separa del browser process por seguridad: el código de la GPU (drivers, shaders) puede tener vulnerabilidades. Si el GPU process falla, no afecta al navegador.

Los renderer processes envían comandos de renderizado al GPU process, que los ejecuta en la GPU y envía el resultado (bitmaps) de vuelta.

9. Comunicación entre procesos (IPC)

Los procesos del navegador se comunican a través de IPC (Inter-Process Communication). En Chrome, se usa Mojo (un sistema de IPC basado en pipes y shared memory). En Firefox, se usa IPDL (Inter-Process Communication Protocol Language).

Los tipos de comunicación son:

La comunicación IPC tiene un costo: cada mensaje requiere serialización, copia, y deserialización. Por eso, los navegadores minimizan la comunicación entre procesos: los renderer processes hacen la mayor parte del trabajo (renderizado, JavaScript) localmente, y solo se comunican con el browser process cuando necesitan recursos externos.

IPC es el cuello de botella

La comunicación entre procesos (IPC) es más costosa que la comunicación entre hilos (memoria compartida). Por eso, los navegadores usan hilos dentro de cada renderer process para el trabajo que requiere comunicación frecuente, y procesos para el aislamiento.

10. Memoria y rendimiento

La arquitectura multiproceso tiene un costo en memoria: cada proceso tiene su propio espacio de direcciones, su propia tabla de páginas, y su propia copia del código. Esto puede consumir mucha memoria en sistemas con muchas pestañas.

Los navegadores mitigan este costo con:

A pesar de estos costos, la arquitectura multiproceso es la estándar porque la seguridad y la estabilidad compensan el consumo de memoria.

11. Un programa que simula la arquitectura de un navegador

El siguiente programa simula la arquitectura de un navegador: un proceso padre (browser) crea procesos hijos (renderers) para cada "pestaña":

navegador_sim.c

#define _POSIX_C_SOURCE 200809L
#include 
#include 
#include 
#include 
#include 

void manejador_sigchld(int sig)
{
    (void) sig;
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main(void)
{
    struct sigaction sa;
    sa.sa_handler = manejador_sigchld;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
    sigaction(SIGCHLD, &sa, NULL);

    printf("Browser (PID %d) iniciado\n", (int) getpid());

    /* Crear 3 "pestañas" (renderer processes) */
    const char *sitios[] = {"sitioA.com", "sitioB.com", "sitioA.com"};
    int n = 3;

    for (int i = 0; i < n; i++) {
        pid_t pid = fork();
        if (pid < 0) { perror("fork"); return 1; }
        if (pid == 0) {
            /* Renderer process: renderizar el sitio */
            printf("  Renderer (PID %d): renderizando %s\n", (int) getpid(), sitios[i]);
            sleep(2);  /* simular renderizado */
            printf("  Renderer (PID %d): %s renderizado\n", (int) getpid(), sitios[i]);
            _exit(0);
        }
        printf("Browser: pestaña %d creada (PID %d, sitio %s)\n", i + 1, (int) pid, sitios[i]);
    }

    /* El browser espera a que todos los renderers terminen */
    while (wait(NULL) > 0);
    printf("Browser: todas las pestañas terminaron\n");
    return 0;
}
terminal - ejecutar navegador_sim

$ gcc -std=c17 -Wall -Wextra -o navegador_sim navegador_sim.c
$ ./navegador_sim
Browser (PID 1090) iniciado
Browser: pestaña 1 creada (PID 1091, sitio sitioA.com)
Browser: pestaña 2 creada (PID 1092, sitio sitioB.com)
Browser: pestaña 3 creada (PID 1093, sitio sitioA.com)
  Renderer (PID 1091): renderizando sitioA.com
  Renderer (PID 1092): renderizando sitioB.com
  Renderer (PID 1093): renderizando sitioA.com
  Renderer (PID 1091): sitioA.com renderizado
  Renderer (PID 1092): sitioB.com renderizado
  Renderer (PID 1093): sitioA.com renderizado
Browser: todas las pestañas terminaron

El programa simula la arquitectura de un navegador: un proceso browser crea renderer processes para cada pestaña. Cada renderer renderiza su sitio independientemente.

En un navegador real, el browser process no espera a los renderers: se ejecutan en paralelo, y el browser gestiona la UI mientras los renderers renderizan.

Observa que sitioA.com aparece en dos pestañas (1091 y 1093). Con site isolation, cada pestaña de sitioA.com tendría su propio proceso. Sin site isolation, ambas compartirían un proceso.

Mientras se ejecuta, puedes observar el árbol de procesos con pstree:

terminal - pstree del navegador simulado

$ pstree -p 1090
navegador_sim(1090)-+-navegador_sim(1091)
                    |-navegador_sim(1092)
                    `-navegador_sim(1093)

El árbol muestra el browser process con 3 renderer processes como hijos. Esto es exactamente la estructura de un navegador real.

12. Resumen

13. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. Por qué los navegadores usan procesos para las pestañas?

Para aislamiento: si una pestaña falla, no afecta a las demás. Para seguridad: un sitio malicioso no puede acceder a la memoria de otras pestañas.

2. Que es el aislamiento de sitios (site isolation)?

Ejecutar cada origen (sitio) en un proceso separado, incluso dentro de la misma pestaña. Protege contra ataques como Spectre.

3. Que hilos tiene un renderer process?

Main thread (HTML, JS), compositor thread (rendering), raster threads (painting), worker threads (Web Workers), IO thread (IPC, red).

4. Que es el sandbox?

Un conjunto de restricciones que impiden que el renderer process acceda a archivos, red u otros procesos. Solo puede comunicarse con el browser process.

5. Por qué el main thread es un cuello de botella?

Porque todo el JavaScript se ejecuta en el main thread. Si un script bloquea el main thread, la página se congela.

6. Que es un Web Worker?

Un hilo separado que ejecuta JavaScript en segundo plano, sin bloquear el main thread. No puede acceder al DOM.

7. Como se comunican los procesos del navegador?

Por IPC: mensajes, shared memory, pipes. Chrome usa Mojo, Firefox usa IPDL.

8. Que es el GPU process?

El proceso que gestiona el acceso a la GPU para renderizado de gráficos, WebGL y decodificación de video.

9. Como mitigan los navegadores el consumo de memoria?

Compartiendo código y páginas de solo lectura, limitando el número de procesos, y suspendiendo pestañas inactivas.

10. Que navegador fue el pionero de la arquitectura multiproceso?

Chrome (2008). Firefox adoptó la arquitectura multiproceso después (2017).

Para practicar

  1. Compila y ejecuta navegador_sim.c. Observa el árbol de procesos con pstree.
  2. Abre Chrome y usa el administrador de tareas (Shift+Esc) para ver los procesos.
  3. Abre chrome://process-internals para ver la arquitectura de procesos.
  4. Crea una página web con un iframe de otro origen y observa los procesos en Chrome.
  5. Escribe un programa que simule site isolation: un proceso por origen.
  6. Investiga cómo Firefox implementa Fission (site isolation).