Planificador de procesos: FCFS, SJF, SRTF y Round Robin
Simula cómo reparte la CPU un sistema operativo con cuatro algoritmos de planificación: diagrama de Gantt con los huecos de CPU ociosa, tiempos de fin, retorno y espera de cada proceso, medias y uso de la CPU. Colas, listas de diccionarios, min con key y simulación paso a paso.
- Simulación por eventos
- Colas con listas
- min con key y tuplas
- Funciones anidadas y nonlocal
- Diccionarios
- f-strings con anchos
Enunciado
Un ordenador ejecuta muchos más procesos que núcleos tiene, así que el planificador del sistema operativo decide en cada momento cuál usa la CPU. La decisión cambia mucho el tiempo que espera cada programa: es uno de los temas que más cae en los exámenes de Sistemas, siempre con el mismo tipo de ejercicio: dibujar el diagrama de Gantt y calcular tiempos.
En este ejercicio vas a escribir ese planificador. Los procesos llegan en distintos instantes con una ráfaga de CPU (lo que necesitan ejecutar), y el programa simula cuatro algoritmos clásicos: FCFS (por orden de llegada), SJF (el de ráfaga más corta entre los que esperan, sin interrumpirlo), SRTF (el de menor tiempo restante, interrumpiendo si llega uno más corto) y Round Robin (turnos de un quantum fijo).
El resultado de cada proceso se mide con dos tiempos: el de retorno (desde que llega hasta que termina) y el de espera (el retorno menos lo que de verdad ha usado la CPU). En la web tienes también el simulador visual de planificación para comprobar tus resultados.
Qué tiene que hacer el programa
- La primera línea es el algoritmo:
FCFS,SJF,SRTFoRR q(con q, el quantum, un entero mayor que 0). Si no es válida, escribeAlgoritmo no válidoy nada más. Cada línea siguiente es un proceso:nombre llegada ráfaga, enteros con la ráfaga mayor que 0. Una línea con otro formato o un nombre repetido escribeAviso: línea N ignorada. Si no queda ningún proceso,No hay procesos. - Desempates: FCFS y Round Robin atienden por orden de llegada y, a igual llegada, por el orden de la entrada. SJF elige la menor ráfaga y SRTF el menor tiempo restante; si empatan, el que llegó antes y luego el de la entrada. En Round Robin, cuando a un proceso se le acaba el quantum, los procesos que han llegado hasta ese instante entran en la cola antes que él.
- SRTF reevalúa cada vez que llega un proceso: si el recién llegado necesita menos que lo que le queda al que está en la CPU, lo expulsa. Si nadie está listo, la CPU queda ociosa hasta la siguiente llegada.
- Escribe
Algoritmo: …con su nombre completo (el de la tabla; en Round Robin,Round Robin con quantum q) yDiagrama: | P1 0-3 | P2 3-7 | … |: cada tramo con el proceso, el inicio y el fin,--para la CPU ociosa, y los tramos seguidos del mismo proceso unidos en uno solo. - Una tabla con la cabecera
Proceso,Llegada,Ráfaga,Fin,Retorno,Esperay una fila por proceso en el orden de la entrada: el nombre en 8 caracteres a la izquierda y los números alineados a la derecha en 8, 8, 6, 9 y 8 caracteres. - Para terminar,
Media de retorno: X · Media de espera: YyUso de la CPU: Z %(tiempo ocupado entre el instante final), con dos decimales y coma decimal.
Entrada
Línea 1: FCFS, SJF, SRTF o RR q. Resto: nombre llegada ráfaga.
Datos de referencia
| Entrada | Algoritmo |
|---|---|
| FCFS | FCFS (primero en llegar, primero en ser servido) |
| SJF | SJF (el más corto primero, sin expulsión) |
| SRTF | SRTF (menor tiempo restante, con expulsión) |
| RR q | Round Robin con quantum q |
Ejemplos de ejecución
Tu programa debe escribir exactamente esta salida para estas entradas. Las pruebas del editor incluyen estos ejemplos y otros casos ocultos.
Round Robin con quantum 2
Entrada
RR 2 P1 0 5 P2 1 3 P3 2 8 P4 3 6
Salida por consola
Algoritmo: Round Robin con quantum 2 Diagrama: | P1 0-2 | P2 2-4 | P3 4-6 | P1 6-8 | P4 8-10 | P2 10-11 | P3 11-13 | P1 13-14 | P4 14-16 | P3 16-18 | P4 18-20 | P3 20-22 | Proceso Llegada Ráfaga Fin Retorno Espera P1 0 5 14 14 9 P2 1 3 11 10 7 P3 2 8 22 20 12 P4 3 6 20 17 11 Media de retorno: 15,25 · Media de espera: 9,75 Uso de la CPU: 100,00 %
SRTF con CPU ociosa
Entrada
SRTF A 0 7 B 2 4 C 4 1 D 5 4 E 30 2 F x 3 A 1 1
Salida por consola
Aviso: línea 7 ignorada Aviso: línea 8 ignorada Algoritmo: SRTF (menor tiempo restante, con expulsión) Diagrama: | A 0-2 | B 2-4 | C 4-5 | B 5-7 | D 7-11 | A 11-16 | -- 16-30 | E 30-32 | Proceso Llegada Ráfaga Fin Retorno Espera A 0 7 16 16 9 B 2 4 7 5 1 C 4 1 5 1 0 D 5 4 11 6 2 E 30 2 32 2 0 Media de retorno: 6,00 · Media de espera: 2,40 Uso de la CPU: 56,25 %
Guía paso a paso
Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.
1. El estado de la simulación
Necesitas el reloj t, la lista de procesos que aún no han llegado (ordenada por llegada), la cola de listos, lo que le queda a cada uno y el diagrama. Una función admitir(t) que pase a la cola los que ya han llegado se usa después de cada paso.
2. Un bucle para todos los algoritmos
Mientras queden procesos: si la cola está vacía, la CPU está ociosa hasta la siguiente llegada; si no, el algoritmo elige uno y lo ejecuta un tiempo. Lo único que cambia entre algoritmos es a quién se elige y durante cuánto.
3. Elegir con min y una tupla
min con una clave en forma de tupla resuelve la elección y los desempates de una vez: primero la ráfaga (o lo que queda), después la llegada y por último el orden de la entrada.
p = min(listos, key=lambda q: (restante[q["nombre"]], q["llegada"], orden[q["nombre"]]))4. Expulsión en SRTF y en Round Robin
En SRTF ejecuta el proceso elegido hasta que termine o hasta la próxima llegada, lo que ocurra antes, y vuelve a elegir. En Round Robin ejecuta como mucho un quantum; después admite a los recién llegados y, si al proceso le queda ráfaga, ponlo al final de la cola.
5. Un diagrama sin tramos repetidos
Al añadir un tramo, si el último es del mismo proceso y acaba justo ahora, alárgalo en lugar de crear otro: en SRTF un proceso puede seguir en la CPU tras una llegada que no lo expulsa.
Resuélvelo aquí
El editor trae el esqueleto del programa. Pulsa «Ejecutar» para comprobarlo con los ejemplos y con 4 casos ocultos que buscan los errores típicos.
Ejemplo
RR 2 P1 0 5 P2 1 3 P3 2 8 P4 3 6
Algoritmo: Round Robin con quantum 2 Diagrama: | P1 0-2 | P2 2-4 | P3 4-6 | P1 6-8 | P4 8-10 | P2 10-11 | P3 11-13 | P1 13-14 | P4 14-16 | P3 16-18 | P4 18-20 | P3 20-22 | Proceso Llegada Ráfaga Fin Retorno Espera P1 0 5 14 14 9 P2 1 3 11 10 7 P3 2 8 22 20 12 P4 3 6 20 17 11 Media de retorno: 15,25 · Media de espera: 9,75 Uso de la CPU: 100,00 %
Solución explicada
Ver la solución completa
1import sys
2
3NOMBRES = {"FCFS": "FCFS (primero en llegar, primero en ser servido)", "SJF": "SJF (el más corto primero, sin expulsión)",
4 "SRTF": "SRTF (menor tiempo restante, con expulsión)", "RR": "Round Robin"}
5
6
7def coma(x):
8 return f"{x:.2f}".replace(".", ",")
9
10
11def simular(algoritmo, quantum, procesos):
12 """Devuelve el diagrama de Gantt como lista de [nombre, inicio, fin] y el instante en que termina cada proceso."""
13 restante = {p["nombre"]: p["rafaga"] for p in procesos}
14 orden = {p["nombre"]: i for i, p in enumerate(procesos)}
15 pendientes = sorted(procesos, key=lambda p: (p["llegada"], orden[p["nombre"]]))
16 listos, gantt, fin = [], [], {}
17 t = 0
18
19 def admitir(hasta):
20 """Pasa a la cola de listos los procesos que han llegado hasta el instante «hasta»."""
21 while pendientes and pendientes[0]["llegada"] <= hasta:
22 listos.append(pendientes.pop(0))
23
24 def ejecutar(p, duracion):
25 nonlocal t
26 if gantt and gantt[-1][0] == p["nombre"] and gantt[-1][2] == t:
27 gantt[-1][2] += duracion # el mismo proceso sigue: se alarga el tramo
28 else:
29 gantt.append([p["nombre"], t, t + duracion])
30 t += duracion
31 restante[p["nombre"]] -= duracion
32
33 admitir(t)
34 while listos or pendientes:
35 if not listos: # CPU ociosa hasta la siguiente llegada
36 gantt.append(["--", t, pendientes[0]["llegada"]])
37 t = pendientes[0]["llegada"]
38 admitir(t)
39 continue
40 if algoritmo == "FCFS":
41 p = listos.pop(0)
42 ejecutar(p, restante[p["nombre"]])
43 elif algoritmo == "SJF":
44 p = min(listos, key=lambda q: (q["rafaga"], q["llegada"], orden[q["nombre"]]))
45 listos.remove(p)
46 ejecutar(p, restante[p["nombre"]])
47 elif algoritmo == "SRTF":
48 p = min(listos, key=lambda q: (restante[q["nombre"]], q["llegada"], orden[q["nombre"]]))
49 # Se ejecuta hasta que acabe o hasta la próxima llegada, que puede expulsarlo
50 hasta = t + restante[p["nombre"]]
51 if pendientes:
52 hasta = min(hasta, pendientes[0]["llegada"])
53 ejecutar(p, hasta - t)
54 if restante[p["nombre"]] > 0:
55 admitir(t)
56 continue
57 listos.remove(p)
58 else: # RR
59 p = listos.pop(0)
60 ejecutar(p, min(quantum, restante[p["nombre"]]))
61 admitir(t) # los que llegan entran en la cola antes que el expulsado
62 if restante[p["nombre"]] > 0:
63 listos.append(p)
64 continue
65 fin[p["nombre"]] = t
66 admitir(t)
67 return gantt, fin
68
69
70def main():
71 lineas = sys.stdin.read().split("\n")
72 cabecera = lineas[0].split()
73 algoritmo = cabecera[0].upper() if cabecera else ""
74 quantum = 0
75 if algoritmo == "RR":
76 if len(cabecera) != 2 or not cabecera[1].isdigit() or int(cabecera[1]) == 0:
77 print("Algoritmo no válido")
78 return
79 quantum = int(cabecera[1])
80 elif algoritmo not in NOMBRES or len(cabecera) != 1:
81 print("Algoritmo no válido")
82 return
83
84 procesos = []
85 for num, linea in enumerate(lineas[1:], start=2):
86 partes = linea.split()
87 if not partes:
88 continue
89 if len(partes) != 3 or not partes[1].isdigit() or not partes[2].isdigit() or int(partes[2]) == 0 \
90 or any(p["nombre"] == partes[0] for p in procesos):
91 print(f"Aviso: línea {num} ignorada")
92 continue
93 procesos.append({"nombre": partes[0], "llegada": int(partes[1]), "rafaga": int(partes[2])})
94 if not procesos:
95 print("No hay procesos")
96 return
97
98 gantt, fin = simular(algoritmo, quantum, procesos)
99 print("Algoritmo: " + NOMBRES[algoritmo] + (f" con quantum {quantum}" if algoritmo == "RR" else ""))
100 print("Diagrama: " + " ".join(f"| {n} {a}-{b}" for n, a, b in gantt) + " |")
101 print(f"{'Proceso':<8}{'Llegada':>8}{'Ráfaga':>8}{'Fin':>6}{'Retorno':>9}{'Espera':>8}")
102 retornos, esperas = [], []
103 for p in procesos:
104 retorno = fin[p["nombre"]] - p["llegada"]
105 espera = retorno - p["rafaga"]
106 retornos.append(retorno)
107 esperas.append(espera)
108 print(f"{p['nombre']:<8}{p['llegada']:>8}{p['rafaga']:>8}{fin[p['nombre']]:>6}{retorno:>9}{espera:>8}")
109 print(f"Media de retorno: {coma(sum(retornos) / len(procesos))} · Media de espera: {coma(sum(esperas) / len(procesos))}")
110 ocupada = sum(b - a for n, a, b in gantt if n != "--")
111 print(f"Uso de la CPU: {coma(100 * ocupada / gantt[-1][2])} %")
112
113
114main()La simulación avanza por eventos: en lugar de mirar el reloj de unidad en unidad, salta de una decisión a la siguiente (fin de un proceso, fin de un quantum o llegada de un proceso). Es más rápido y deja claro en qué instantes decide el planificador.
Los cuatro algoritmos comparten el bucle y las funciones admitir y ejecutar; solo cambia la elección del proceso y el tiempo que se le deja. Es la misma estructura que usan los sistemas operativos reales, donde la política de planificación es un módulo intercambiable.
Las tuplas como clave de min codifican la política y los desempates en una sola expresión, y hacen que el resultado sea siempre el mismo para la misma entrada, sin depender del orden en que se guardan los procesos.
Retorno y espera salen solos al final: retorno = fin − llegada y espera = retorno − ráfaga. Comparar las medias entre algoritmos con los mismos procesos muestra por qué SJF y SRTF minimizan la espera media y por qué Round Robin, aunque espere más de media, responde antes a los procesos cortos.
Para ir más allá
- Añade la planificación por prioridades (con y sin expulsión) y el envejecimiento para evitar la inanición.
- Calcula también el tiempo de respuesta (desde que llega hasta que usa la CPU por primera vez) de cada proceso.
- Simula el coste del cambio de contexto: suma 1 unidad cada vez que la CPU pasa de un proceso a otro y compara Round Robin con quantums de 1, 2 y 4.