¿Qué es un sistema operativo?

"Un sistema operativo es como un buen mesero: si lo notás, está fallando." — anónimo, sistemas operativos.

Qué vas a aprender en este capítulo

Vas a entender qué es un sistema operativo (SO) y qué problemas resuelve. Vas a aprender la diferencia entre kernel (el corazón del SO) y todo lo demás (shell, librerías, aplicaciones). Vas a ver qué pasa cuando ejecutás ls en la terminal — y qué cambia entre eso y ejecutar print("hola") en Python. Y vas a conocer la historia: por qué los SO modernos son como son, y por qué Unix dominó tanto que sin él no existiría hoy ni Linux ni macOS ni Android.

1.1 La idea: el mesero invisible

💡 Intuición

Imaginate un restaurante caótico. Cien clientes pidiendo distintos platos al mismo tiempo. Un solo cocinero. Sin orden, sería el desastre absoluto: el cocinero ignoraría a unos, atendería tres veces a otros, los platos saldrían fríos, las cuentas mal hechas.

Ahora imaginá lo mismo con un buen mesero entre el cliente y la cocina:

  • El mesero recibe los pedidos en orden.
  • Decide qué pasa primero al cocinero.
  • Lleva la cuenta separada de cada mesa.
  • Si dos clientes piden lo mismo, los dos quedan satisfechos.
  • Si un cliente quiere modificar su pedido, el mesero lo coordina.
  • Si alguien intenta robar comida ajena, el mesero lo bloquea.

Eso es exactamente un sistema operativo. La cocina es el hardware (CPU, memoria, disco). Los clientes son los programas. El mesero — el SO — coordina todo, oculta el caos, y le entrega a cada programa la ilusión de ser el único cliente del restaurante.

Un sistema operativo es la capa de software entre el hardware y las aplicaciones. Su trabajo es administrar recursos, dar abstracciones útiles, y proteger a unos programas de otros.

Sin SO, cada programa tendría que saber qué CPU tenés, qué memoria, qué disco, qué impresora — y entenderse con todos los demás programas. Imposible. El SO le dice a cada programa: "yo me encargo, vos solo decime qué necesitás".

📜 Historia

Los primeros computadores (1940s-50s) no tenían sistema operativo. Cargabas tu programa en cinta perforada, lo ejecutabas, recogías el resultado en papel, y le tocaba al siguiente. Una máquina servía un programa por turno. Si tu programa fallaba, perdías el turno.

En 1956, GM y IBM crearon el primer sistema por lotes (batch monitor) para el IBM 704: un programita pequeño que cargaba programas en orden, los ejecutaba uno tras otro y descargaba resultados sin intervención humana. Fue el primer "sistema operativo".

En los 60 llegó la multiprogramación: dos programas en RAM al mismo tiempo, alternándose en la CPU. Multics (MIT, Bell Labs y GE, 1965) intentó hacerlo a gran escala — fue ambicioso, complejo, lento, parcialmente exitoso.

En 1969, Ken Thompson y Dennis Ritchie, frustrados con Multics, escribieron una versión más simple en una PDP-7 que tenían tirada en Bell Labs. Lo llamaron UNICS (un chiste sobre Multics) — después Unix.

Unix tenía:

  • Un kernel pequeño y simple.
  • Un shell para interactuar.
  • Archivos como bytes (sin estructura impuesta).
  • "Todo es un archivo" — incluso dispositivos.
  • Pipes para combinar programas.

Esa filosofía — herramientas chicas que se combinan — definió la informática. Linux, macOS, BSD, Android, iOS son todos descendientes de Unix. Windows es un primo lejano (su ancestro VMS comparte autores con Windows NT).

Para aprender SO bien hoy, estudiar Unix es estudiar el 90% del territorio.

1.2 Las tres funciones del SO

📐 Fundamento

Todo SO hace tres cosas:

1. Gestor de recursos

Hay un solo CPU (o pocos). Hay 16 GB de RAM. Hay un disco. Hay decenas de programas que quieren todo eso al mismo tiempo. El SO decide quién obtiene qué y por cuánto tiempo.

  • CPU: divide el tiempo en pedacitos (cuantos) y rota entre programas.
  • Memoria: asigna regiones a cada programa, y le da la ilusión de tener más con memoria virtual.
  • Disco: gestiona archivos, permisos, espacio.
  • Dispositivos: colas para impresora, GPU, red, etc.

2. Capa de abstracción

El hardware es horrible de programar. Tu disco SSD habla en bloques de 512 bytes; tu impresora en comandos PostScript; tu placa de red en frames Ethernet. Si cada programa tuviera que saber esos detalles, programar sería imposible.

El SO te da abstracciones simples:

  • Archivo: una secuencia de bytes con nombre. (Por debajo, son bloques fragmentados en distintos lugares del disco.)
  • Proceso: un programa en ejecución. (Por debajo, son páginas de memoria, descriptores, registros guardados.)
  • Socket de red: una "tubería" entre dos máquinas. (Por debajo, son paquetes IP, fragmentación, retransmisión.)

Tu programa pide open("archivo.txt") y listo — no le importa si está en SSD, HDD, NVMe, USB o red.

3. Aislamiento y seguridad

Sin SO, un programa podría leer la memoria de otro, sobrescribirla, robar contraseñas, dañar archivos del SO. Con SO:

  • Cada proceso tiene su propio espacio de memoria, aislado del resto.
  • Los permisos de archivos restringen quién lee/escribe qué.
  • Los modos de ejecución (user/kernel) impiden que un programa común haga cosas peligrosas.
  • Las llamadas al sistema son la única puerta para acceder al hardware — el SO valida cada una.

Sin esa función, internet no existiría: confiar en código ajeno (un sitio web, un email) sería suicida.

1.3 Anatomía: kernel, shell, syscalls

📐 Fundamento

El sistema operativo no es una sola cosa. Tiene varias capas. De adentro hacia afuera:

+----------------------------------+
|   Aplicaciones (Firefox, VLC)    |   ← modo usuario
+----------------------------------+
|   Bibliotecas (libc, glibc)      |   ← modo usuario
+----------------------------------+
|   Llamadas al sistema (syscalls) |   ← frontera
+----------------------------------+
|   Kernel (gestor de recursos)    |   ← modo kernel
+----------------------------------+
|   Drivers de dispositivos        |   ← modo kernel
+----------------------------------+
|   Hardware (CPU, RAM, disco)     |
+----------------------------------+

Kernel

El kernel es el corazón del SO. Es un programa que se carga al iniciar la computadora y queda corriendo siempre, en una región protegida de memoria.

Funciones del kernel:

  • Gestionar procesos (crear, destruir, planificar).
  • Gestionar memoria (asignar, proteger, paginar).
  • Gestionar archivos (sistemas de archivos, permisos).
  • Comunicarse con dispositivos (vía drivers).
  • Servir las llamadas al sistema (syscalls).

Lo que NO es el kernel: las aplicaciones, el shell, las bibliotecas estándar, el navegador. Todos eso son programas de usuario que corren encima del kernel.

Shell

El shell es un intérprete de comandos. Es un programa más que corre encima del kernel — confundir shell con kernel es uno de los errores más comunes de principiantes.

Cuando abrís una terminal y escribís ls, lo que pasa es:

  1. El shell (bash, zsh, fish) recibe el texto.
  2. Identifica que querés ejecutar ls.
  3. Pide al kernel crear un nuevo proceso (fork).
  4. Carga el programa /bin/ls (exec).
  5. El proceso ls se ejecuta y muestra los archivos.
  6. El shell espera (wait) a que termine.
  7. Recibe el control de vuelta y muestra el prompt.

Distintos shells son distintos programas. Bash, Zsh, Fish, PowerShell — son intercambiables. Tu computadora puede tener varios instalados.

Syscalls (llamadas al sistema)

Es la interfaz por la que un programa común le pide al kernel que haga algo. Son las únicas puertas autorizadas:

Syscall (POSIX) Para qué
read(fd, buf, n) Leer de un archivo
write(fd, buf, n) Escribir
open(path, flags) Abrir un archivo
close(fd) Cerrar
fork() Crear un proceso hijo
exec(path, args) Reemplazar el proceso actual con otro programa
wait(pid, status) Esperar a que un proceso termine
mmap(addr, len, ...) Mapear memoria
socket(...) Crear endpoint de red
kill(pid, sig) Enviar señal a un proceso

Cuando llamás print("hola") en Python, eso eventualmente llama a write(1, "hola\n", 5) (syscall write) — el kernel toma el control, escribe los bytes en la terminal, y devuelve el control.

Las llamadas a syscalls son CARAS. Implican:

  1. Cambiar de modo usuario a modo kernel (un cambio costoso de contexto).
  2. Validar parámetros.
  3. Hacer el trabajo.
  4. Volver a modo usuario.

Por eso optimizaciones como buffering (juntar varios write en uno solo) son tan valiosas. Es la razón por la que escribir 1000 caracteres uno por uno con write es 100× más lento que escribir los 1000 de una.

1.4 Modo usuario vs modo kernel

📐 Fundamento

La CPU moderna tiene (al menos) dos modos de ejecución:

Modo Quién corre Qué puede hacer
Usuario Aplicaciones, shell, librerías Aritmética, leer/escribir su propia memoria, llamar syscalls
Kernel (a.k.a. supervisor, ring 0) Solo el kernel TODO: acceder hardware, modificar tablas de página, ejecutar instrucciones privilegiadas

Por qué dos modos. Si todos los programas pudieran hacer todo, un programa malicioso (o con un bug) podría:

  • Escribir directamente al disco y borrar el SO.
  • Apagar la computadora.
  • Leer la memoria de otros programas.
  • Reprogramar el reloj.

La separación hace que un programa común NO puede hacer esas cosas. Si lo intenta, la CPU le dispara una excepción y el kernel lo termina (matá a Java con kill -9 cuando se cuelga — eso pasa por una excepción).

Cómo se cambia de modo. El modo usuario solo puede cambiar a kernel mediante:

  1. Llamadas al sistema (syscall) — voluntario y controlado.
  2. Excepciones (división por cero, acceso a memoria inválida) — involuntario.
  3. Interrupciones (un dispositivo avisa que terminó) — externo.

En cualquiera de los tres casos, el control salta a una dirección fija del kernel, que decide qué hacer.

Ver los modos en Linux. El comando time muestra cuánto tiempo gastaste en cada modo:

$ time find /usr -name "*.txt" > /dev/null

real    0m2.453s
user    0m0.521s   ← tiempo en modo usuario
sys     0m1.873s   ← tiempo en modo kernel

find recorre archivos: la mayoría del trabajo (leer directorios) es del kernel. Si un programa pasa mucho tiempo en sys, probablemente hace muchas syscalls — buen punto para optimizar.

1.5 Arquitecturas de kernel

📐 Fundamento

Hay varias formas de organizar un kernel. Los dos extremos:

Monolítico

Todo el kernel — gestión de procesos, memoria, archivos, drivers, red — está en un solo bloque ejecutándose en modo kernel.

  • Rápido. Llamadas internas son llamadas a función directas.
  • Simple de implementar.
  • ❌ Un bug en un driver puede tumbar todo el sistema.
  • ❌ Difícil de mantener cuando crece (Linux tiene ~30 millones de líneas de código).

Ejemplos: Linux, MS-DOS, FreeBSD.

Microkernel

Solo lo esencial corre en modo kernel: planificación, memoria, IPC. Todo lo demás (drivers, sistemas de archivos, red) corre como procesos de usuario que se comunican con el microkernel mediante mensajes.

  • Aislamiento. Si el driver de impresora se cae, no se cae el kernel.
  • Fácil de extender y verificar (kernels chicos formalmente).
  • Más lento — paso de mensajes entre procesos es caro.
  • ❌ Más complejo de diseñar bien.

Ejemplos: MINIX 3, L4, QNX (lo usa BlackBerry, autos).

Híbrido

Mezcla de los dos: un núcleo pequeño con varios subsistemas privilegiados.

Ejemplos: Windows NT (incluye XP, Vista, 7, 10, 11), macOS (XNU = Mach + BSD).

El gran debate

En 1992, Andrew Tanenbaum (creador de MINIX) y Linus Torvalds (creador de Linux) tuvieron un debate público en Usenet sobre si Linux era "obsoleto" por ser monolítico. Tanenbaum favorecía el microkernel; Linus prefería la simplicidad y velocidad del monolítico. Ganó la historia: Linux ganó adopción masiva. El debate sigue vivo: la academia ama microkernels, la industria usa monolíticos.

1.6 Cómo arranca una computadora

🛠️ En la práctica

Cuando prendés tu computadora, esto pasa en orden:

  1. POST (Power-On Self-Test). El firmware (BIOS o UEFI) en una ROM revisa el hardware: RAM ok, disco ok, teclado ok.
  2. Bootloader. El firmware busca un programa cargador (GRUB en Linux, Windows Boot Manager) en el disco y lo ejecuta.
  3. Carga del kernel. El bootloader carga el archivo del kernel (en Linux es /boot/vmlinuz-...) en memoria y le pasa el control.
  4. Kernel se inicia. Detecta hardware, carga drivers, monta el sistema de archivos raíz.
  5. Init. El kernel ejecuta el primer proceso de usuario (PID 1, históricamente init, hoy systemd en Linux).
  6. Init lanza servicios. Servicios de fondo (cron, syslog, sshd, redes), después el gestor de login (GDM, SDDM, login en consola).
  7. Login. Vos te logueás. Se ejecuta tu shell o tu escritorio gráfico (GNOME, KDE).

Todo esto pasa en segundos. Y cada paso construye encima del anterior.

Comando útil: dmesg muestra los mensajes que el kernel imprimió al arrancar:

$ dmesg | head
[    0.000000] Linux version 6.6.0 ...
[    0.001234] CPU: Intel(R) Core(TM) i5-...
[    0.234567] EFI: ...

Es como leer el diario de cuando despertó tu computadora.

1.7 Tipos de SO

Tipo Características Ejemplos
Sistemas batch Ejecutan trabajos en lote, sin interacción OS/360 (1960s)
Time-sharing Varios usuarios comparten una máquina Unix original
Personal Un usuario, una máquina Windows, macOS, Ubuntu
Tiempo real (RTOS) Garantizan respuesta en plazo VxWorks, FreeRTOS (en autos, aviones)
Embebidos Recursos limitados, propósito específico RTOS chicos, microcontroladores
Distribuidos Una "vista" de muchas máquinas Plan 9
Móviles Optimizado para batería, táctil Android, iOS
Servidor Multi-usuario, alto throughput Linux (Ubuntu Server, CentOS), Windows Server

Aunque parezcan distintos, comparten la mayoría de los conceptos: procesos, hilos, memoria, archivos. Lo que cambia es el énfasis: un RTOS prioriza respuesta predecible, un SO de servidor maximiza throughput, un SO móvil cuida batería.

1.8 Aplicación práctica: medir el costo de una syscall

🛠️ En la práctica

Programa simple en C que mide cuánto tarda una syscall trivial (getpid) frente a una llamada de función normal:

#include <stdio.h>
#include <unistd.h>
#include <time.h>

int main() {
    int n = 1000000;
    struct timespec a, b;
    long fake;

    /* 1. Llamada de función normal (no syscall) */
    clock_gettime(CLOCK_MONOTONIC, &a);
    for (int i = 0; i < n; i++) fake = i;
    clock_gettime(CLOCK_MONOTONIC, &b);
    printf("Loop normal:    %.2f ns/iter\n",
           (b.tv_nsec - a.tv_nsec) / 1.0);

    /* 2. Syscall: getpid devuelve el PID actual */
    clock_gettime(CLOCK_MONOTONIC, &a);
    for (int i = 0; i < n; i++) (void)getpid();
    clock_gettime(CLOCK_MONOTONIC, &b);
    printf("getpid syscall: %.2f ns/iter\n",
           (b.tv_nsec - a.tv_nsec) / 1.0);

    return 0;
}

Resultados típicos (en una máquina moderna):

Loop normal:    1.0 ns/iter
getpid syscall: 50.0 ns/iter

Una syscall trivial es ~50× más cara que una operación común. Por eso conviene agrupar trabajo: en vez de 1000 syscalls de 1 byte, una sola de 1000 bytes.

Nota: Linux moderno hace que getpid sea súper rápida vía vDSO (un truco que evita el cambio de modo). Para una syscall "real" como read(0, ...) el costo es mayor. La lección no cambia.

1.9 Resumen visual

Concepto Una línea
Sistema operativo Software entre hardware y aplicaciones que gestiona recursos.
Kernel Núcleo del SO; corre en modo privilegiado.
Shell Intérprete de comandos. Es un programa, no el SO.
Syscall Puerta autorizada de un programa al kernel.
Modo usuario Limitado, donde corren las apps.
Modo kernel Privilegiado, donde corre el kernel.
Monolítico Todo el kernel en un bloque (Linux).
Microkernel Kernel mínimo + servicios separados (MINIX).

1.10 Ejercicios

✏️ Ejercicio 1.1 — Identificar capas

Para cada acción, identificá si está en modo usuario o modo kernel:

a. Sumar dos números en un programa Python. b. Escribir un texto a un archivo. c. Calcular el factorial recursivamente. d. Crear un nuevo proceso con fork(). e. Mostrar la pantalla. f. Renderizar JavaScript en el navegador.

✏️ Ejercicio 1.2 — Distinción shell vs kernel

Decí si cada uno es shell, kernel, biblioteca, o aplicación:

a. Bash b. Linux c. libc d. Firefox e. systemd f. zsh g. ps (/bin/ps)

✏️ Ejercicio 1.3 — Cuenta las syscalls

En Linux, la herramienta strace te muestra todas las syscalls que hace un programa. Ejecutá:

$ strace -c ls 2>&1 | tail -20

¿Cuáles syscalls aparecen más? ¿Qué hacen?

✏️ Ejercicio 1.4 — Arquitectura

Un equipo discute si reescribir su SO de un kernel monolítico a un microkernel. Listá:

a. Tres ventajas del cambio. b. Tres desventajas. c. ¿En qué tipo de proyecto el cambio sería buena idea? ¿En cuál no?

1.11 Para profundizar


Definiciones nuevas: sistema operativo, kernel, shell, syscall, modo usuario, modo kernel, kernel monolítico, microkernel, bootloader, init.