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:
- Sin aislamiento: un error en una pestaña (un segfault en un plugin) podía colgar todo el navegador.
- Sin seguridad: un sitio malicioso podía acceder a la memoria de otras pestañas o del navegador.
- Sin paralelismo: un solo hilo bloqueado (por ejemplo, en una operación de red) bloqueaba todo el navegador.
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.
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:
| Proceso | Función | Cantidad |
|---|---|---|
| Browser process | Interfaz de usuario, gestión de pestañas, red, disco, seguridad. | 1 |
| Renderer process | Renderizado de una pestaña o sitio. Ejecuta HTML, CSS, JavaScript. | 1 por pestaña o sitio |
| GPU process | Renderizado de gráficos (composición, WebGL). | 1 |
| Plugin process | Ejecución de plugins (Flash, PDF, etc.). | 1 por plugin |
| Utility process | Tareas 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.
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:
- Aislamiento: si una pestaña falla (segfault en JavaScript), no afecta a las demás. El navegador muestra "Aw, snap!" en la pestaña fallida y las demás siguen funcionando.
- Seguridad: un sitio malicioso no puede acceder a la memoria de otras pestañas ni del navegador.
- Robustez: un plugin (como Flash) que falla no cuelga todo el navegador.
- Paralelismo: cada proceso puede ejecutarse en un núcleo diferente.
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 (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 isolation | Con site isolation |
|---|---|
| Una pestaña = un proceso | Un origen = un proceso |
| Los iframes comparten proceso | Cada iframe tiene su propio proceso |
| Vulnerable a Spectre | Protegido contra Spectre |
| Menos memoria | Má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:
| Hilo | Función |
|---|---|
| Main thread | Parsea HTML, ejecuta JavaScript, construye el DOM. Es el hilo principal y el único que puede modificar el DOM. |
| Compositor thread | Compone las capas del renderizado. Permite scroll suave sin bloquear el main thread. |
| Raster threads | Pintan las capas en bitmaps. Se ejecutan en paralelo para acelerar el renderizado. |
| Worker threads | Ejecutan Web Workers (JavaScript en segundo plano) y Service Workers. |
| IO thread | Maneja 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.
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:
- Interfaz de usuario: la barra de direcciones, los botones, las pestañas.
- Gestión de pestañas: crear, cerrar, cambiar de pestaña.
- Red: descargar recursos (HTML, CSS, JS, imágenes).
- Disco: caché, cookies, historial, almacenamiento local.
- Seguridad: política de mismo origen, certificados SSL, permisos.
- Comunicación con renderer processes: enviar comandos y recibir resultados.
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 sandbox | Con sandbox |
|---|---|
| Acceso a archivos del sistema | Sin acceso (solo a través del browser process) |
| Acceso a la red | Sin acceso (solo a través del browser process) |
| Acceso a otros procesos | Sin acceso |
| Ejecución de código nativo | Restringida |
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:
- Mensajes: el browser process envía comandos al renderer process (por ejemplo, "navegar a esta URL").
- Shared memory: para datos grandes (como bitmaps de renderizado), se usa memoria compartida para evitar copiar.
- Pipes: para comunicación bidireccional entre procesos.
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.
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:
- Compartir código: el código del navegador (librerías, etc.) se comparte entre procesos (como cualquier programa).
- Compartir memoria de solo lectura: las páginas de código y datos de solo lectura se comparten.
- Limitar el número de procesos: Chrome limita el número de renderer processes (por ejemplo, 1 por sitio, no 1 por pestaña).
- Suspender pestañas inactivas: Chrome suspende las pestañas que no se usan, liberando memoria.
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":
#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;
}
$ 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:
$ 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
- Los navegadores modernos usan una arquitectura multiproceso.
- El browser process gestiona la UI, la red y la seguridad.
- Cada pestaña o sitio tiene su propio renderer process.
- El aislamiento de sitios (site isolation) ejecuta cada origen en un proceso separado.
- Cada renderer process tiene múltiples hilos (main, compositor, raster, worker, IO).
- El sandbox restringe lo que un renderer process puede hacer.
- Los procesos se comunican por IPC (mensajes, shared memory, pipes).
- El main thread es el cuello de botella: todo el JavaScript se ejecuta ahí.
- Los Web Workers permiten ejecutar JS en hilos separados.
- Chrome fue el pionero de la arquitectura multiproceso (2008).
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
- Compila y ejecuta navegador_sim.c. Observa el árbol de procesos con pstree.
- Abre Chrome y usa el administrador de tareas (Shift+Esc) para ver los procesos.
- Abre chrome://process-internals para ver la arquitectura de procesos.
- Crea una página web con un iframe de otro origen y observa los procesos en Chrome.
- Escribe un programa que simule site isolation: un proceso por origen.
- Investiga cómo Firefox implementa Fission (site isolation).