Semana 1 — Introducción y procesos
Objetivos
- Entender qué hace un OS y por qué lo estudiamos en tres piezas: virtualización, concurrencia y persistencia.
- Definir proceso (process), su estado y las estructuras que el OS mantiene por cada uno.
- Dejar listo el entorno de trabajo para todo el semestre.
Sesión A — Presentación del curso · Introducción
Lecturas:
- Prefacio (opcional)
- Cap. 1 Diálogo
- Cap. 2 Introduction to Operating Systems · código
Temas:
- Programa, calendario y políticas (SYLLABUS).
- Qué es un OS: virtualización de recursos, llamadas al sistema (system calls), biblioteca estándar.
- Demostraciones del cap. 2:
cpu.c,mem.c,threads.c,io.c. - Breve historia: sistemas por lotes (batch), multiprogramación, tiempo compartido (time sharing).
- Cómo se organiza un kernel: monolítico (Linux, xv6), por capas (THE), microkernel (MINIX 3, seL4, QNX), módulos cargables de Linux e híbridos (XNU, Windows NT); el costo del IPC en el camino de un
read(); el debate Tanenbaum–Torvalds.
Lecturas complementarias (estructura del kernel, que OSTEP no cubre):
- Libro de xv6: xv6: a simple, Unix-like teaching operating system, cap. 2, secs. 2.3 Kernel organization y 2.4 Code: xv6 organization.
- J. N. Herder et al., MINIX 3: a highly reliable, self-repairing operating system (2006).
- G. Klein et al., seL4: Formal Verification of an OS Kernel (SOSP 2009), secs. 1 y 2.
- The Tanenbaum-Torvalds Debate: los mensajes originales de 1992, en el apéndice A del libro Open Sources.
- Módulos de Linux: la página man de
lsmod(8)y la documentación del kernel Building External Modules. - Opcional: Tanenbaum y Bos, Modern Operating Systems, 5.ª ed., sec. 1.7 Operating System Structure.
Sesión B — Procesos
Lecturas:
- Cap. 3 Diálogo sobre virtualización
- Cap. 4 The Abstraction: The Process
Temas:
- Proceso vs. programa. Espacio de direcciones (address space), registros, PC.
- Estados: ejecutando (running), listo (ready), bloqueado (blocked).
- Bloque de control de proceso (PCB) y lista de procesos;
struct procen xv6. - Mecanismo vs. política (mechanism vs. policy).
Laboratorio — Entorno de trabajo
Guía: labs/README.md
- Instalar Docker y levantar el contenedor del curso (
env/run.sh). - Clonar
ostep-homeworkyostep-projects. - Repaso de C en Linux con el Lab Tutorial: compilar con
gcc -Wall,make,gdb,man. - Simulador
process-run.py: preguntas 1–4 del cap. 4.
Entregas
- P0 publicado: Utilidades Unix (
wcat,wgrep,wzip,wunzip). Entrega en semana 3.
Preguntas de repaso
- ¿Qué significa que el OS “virtualiza” la CPU? ¿Qué ilusión le da a cada programa?
- Si un proceso hace E/S y se bloquea, ¿qué debería hacer el OS con la CPU? Use
process-run.py -Ipara comparar las opciones. - Dé un ejemplo de mecanismo y uno de política dentro de un OS.
- Un
read()que va al disco, ¿cuántas veces entra y sale del kernel en un kernel monolítico y en un microkernel? ¿Qué gana el microkernel a cambio?
Código de la semana
Compilar dentro del contenedor del curso con make o gcc -Wall.
Ejemplos
Los programas que usan los apuntes y la clase.
// common.h — utilidades pequeñas para los ejemplos de la semana 1.
#ifndef COMMON_H
#define COMMON_H
#include <time.h>
// Espera activa: ocupa la CPU durante `segundos`.
static inline void spin(double segundos) {
struct timespec t0, t;
clock_gettime(CLOCK_MONOTONIC, &t0);
do {
clock_gettime(CLOCK_MONOTONIC, &t);
} while ((t.tv_sec - t0.tv_sec) + (t.tv_nsec - t0.tv_nsec) / 1e9 < segundos);
}
#endif// cpu.c — virtualización de la CPU.
// Ejecuten varias copias a la vez: ./cpu A & ./cpu B & ./cpu C &
// Con un solo núcleo físico, las tres avanzan "al mismo tiempo".
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "uso: cpu <texto>\n");
return 1;
}
for (int i = 0; i < 5; i++) {
spin(1);
printf("%s\n", argv[1]);
fflush(stdout);
}
return 0;
}// io.c — persistencia: escribir un archivo y forzarlo al disco.
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(void) {
int fd = open("hola.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
return 1;
}
const char *msg = "hola, disco\n";
if (write(fd, msg, strlen(msg)) != (ssize_t)strlen(msg)) {
perror("write");
return 1;
}
fsync(fd); // sin esto, los datos podrían quedar solo en la caché del kernel
close(fd);
printf("listo: hola.txt\n");
return 0;
}// mem.c — virtualización de la memoria.
// Dos copias imprimen la MISMA dirección pero valores independientes:
// setarch -R ./mem 0 & setarch -R ./mem 100 &
// (setarch -R desactiva la aleatorización de direcciones para verlo mejor.)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "common.h"
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "uso: mem <valor inicial>\n");
return 1;
}
int *p = malloc(sizeof(int));
if (p == NULL) {
perror("malloc");
return 1;
}
*p = atoi(argv[1]);
printf("(%d) dirección: %p\n", getpid(), (void *)p);
for (int i = 0; i < 4; i++) {
spin(1);
*p = *p + 1;
printf("(%d) *p = %d\n", getpid(), *p);
}
free(p);
return 0;
}// threads.c — concurrencia: dos hilos incrementan un contador compartido.
// ./threads 1000 casi siempre imprime 2000
// ./threads 1000000 casi nunca imprime 2000000. ¿Por qué?
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
static volatile long counter = 0;
static long loops;
static void *worker(void *arg) {
(void)arg;
for (long i = 0; i < loops; i++)
counter++; // leer, sumar, escribir: tres pasos, no uno
return NULL;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "uso: threads <iteraciones>\n");
return 1;
}
loops = atol(argv[1]);
pthread_t a, b;
pthread_create(&a, NULL, worker, NULL);
pthread_create(&b, NULL, worker, NULL);
pthread_join(a, NULL);
pthread_join(b, NULL);
printf("esperado: %ld\nobtenido: %ld\n", 2 * loops, counter);
return 0;
}Compilación
Cómo se compilan y ejecutan los ejemplos.
# Semana 1: las cuatro demostraciones del cap. 2 de OSTEP.
# make compila todo
# make test corre cada demostración una vez (unos 10 s)
# make clean borra los binarios
CFLAGS = -Wall -Wextra -O1 -g
LDLIBS = -pthread
PROGS = cpu mem threads io
all: $(PROGS)
$(PROGS): common.h
test: $(PROGS)
./cpu A
./mem 0
./threads 1000
./io
cat hola.txt
clean:
rm -f $(PROGS) hola.txt
.PHONY: all test clean