01
Introducción

Introducción y procesos

Introducción · Virtualización de la CPU

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:

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):

Sesión B — Procesos

Lecturas:

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 proc en xv6.
  • Mecanismo vs. política (mechanism vs. policy).

Laboratorio — Entorno de trabajo

Guía: labs/README.md

  1. Instalar Docker y levantar el contenedor del curso (env/run.sh).
  2. Clonar ostep-homework y ostep-projects.
  3. Repaso de C en Linux con el Lab Tutorial: compilar con gcc -Wall, make, gdb, man.
  4. 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

  1. ¿Qué significa que el OS “virtualiza” la CPU? ¿Qué ilusión le da a cada programa?
  2. Si un proceso hace E/S y se bloquea, ¿qué debería hacer el OS con la CPU? Use process-run.py -I para comparar las opciones.
  3. Dé un ejemplo de mecanismo y uno de política dentro de un OS.
  4. 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 (5)Compilación (1)

Ejemplos

Los programas que usan los apuntes y la clase.

common.hDescargar common.h
// 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.cDescargar cpu.c
// 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.cDescargar io.c
// 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.cDescargar mem.c
// 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.cDescargar threads.c
// 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.

MakefileDescargar Makefile
# 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