1. Por qué un simulador conjunto
Ver las tres variantes por separado obliga a recordar las métricas de una para compararlas con otra. Un simulador conjunto ejecuta las tres sobre la misma carga de trabajo y muestra los resultados uno debajo del otro, lo que hace la comparación inmediata.
Las ventajas son las mismas que en el simulador de SJF/SRTF (tema 16): comparabilidad, visualización y comprensión. La diferencia es que aquí las variantes son tres (no apropiativa, apropiativa, con envejecimiento) en lugar de dos.
La carga de trabajo es idéntica para las tres variantes. Si se generaran cargas distintas, la comparación no sería válida.
2. Arquitectura
El simulador tiene las mismas partes que los anteriores, pero con tres funciones de algoritmo:
| Parte | Responsabilidad | Funciones |
|---|---|---|
| Modelo | Representar un proceso con prioridad. | crearProceso, copiar |
| Generador | Producir cargas reproducibles con prioridades. | crearGenerador, generarCarga |
| No apropiativa | Prioridades sin interrupción. | prioridadesNoAprop |
| Apropiativa | Prioridades con interrupción. | prioridadesAprop |
| Con envejecimiento | Prioridades con envejecimiento. | prioridadesAging |
| Vista | Dibujar Gantt y tabla para cada variante. | dibujarGantt, renderTabla |
| Controlador | Leer controles, ejecutar las tres, actualizar la vista. | dibujar |
3. Prioridades no apropiativas
La versión no apropiativa elige, en cada decisión, el proceso listo con la mayor prioridad. No interrumpe procesos en curso:
function prioridadesNoAprop(procesos) {
var p = procesos.map(function (x) { return copiar(x); });
var t = 0;
var ejecutados = [];
var restantes = p.slice();
while (ejecutados.length < p.length) {
var disponibles = restantes.filter(function (x) { return x.llegada <= t; });
if (disponibles.length === 0) {
var sig = restantes.reduce(function (a, b) {
return a.llegada < b.llegada ? a : b;
});
t = sig.llegada;
disponibles = restantes.filter(function (x) { return x.llegada <= t; });
}
var elegido = disponibles.reduce(function (a, b) {
if (a.prioridad !== b.prioridad) return a.prioridad > b.prioridad ? a : b;
if (a.llegada !== b.llegada) return a.llegada < b.llegada ? a : b;
return a.pid < b.pid ? a : b;
});
restantes = restantes.filter(function (x) { return x !== elegido; });
if (t < elegido.llegada) t = elegido.llegada;
elegido.inicio = t;
elegido.fin = t + elegido.rafaga;
t = elegido.fin;
elegido.respuesta = elegido.inicio - elegido.llegada;
elegido.retorno = elegido.fin - elegido.llegada;
elegido.espera = elegido.retorno - elegido.rafaga;
ejecutados.push(elegido);
}
return ejecutados;
}
4. Prioridades apropiativas
La versión apropiativa interrumpe al proceso en curso si llega uno con mayor prioridad. Necesita mantener el tiempo restante:
function prioridadesAprop(procesos) {
var restantes = procesos.map(function (x) {
return { pid: x.pid, llegada: x.llegada, rafaga: x.rafaga,
prioridad: x.prioridad, restante: x.rafaga,
inicio: 0, fin: 0, respuesta: 0, espera: 0, retorno: 0,
primera: true };
});
var ejecutados = [];
var t = 0;
while (ejecutados.length < procesos.length) {
var disponibles = restantes.filter(function (x) { return x.llegada <= t; });
if (disponibles.length === 0) {
var sig = restantes.reduce(function (a, b) {
return a.llegada < b.llegada ? a : b;
});
t = sig.llegada;
disponibles = restantes.filter(function (x) { return x.llegada <= t; });
}
var elegido = disponibles.reduce(function (a, b) {
if (a.prioridad !== b.prioridad) return a.prioridad > b.prioridad ? a : b;
if (a.restante !== b.restante) return a.restante < b.restante ? a : b;
if (a.llegada !== b.llegada) return a.llegada < b.llegada ? a : b;
return a.pid < b.pid ? a : b;
});
var proximaLlegada = Infinity;
restantes.forEach(function (x) {
if (x.llegada > t && x.llegada < proximaLlegada) proximaLlegada = x.llegada;
});
var duracion = Math.min(elegido.restante,
isFinite(proximaLlegada) ? proximaLlegada - t : elegido.restante);
if (duracion <= 0) duracion = elegido.restante;
if (!isFinite(duracion)) duracion = elegido.restante;
if (elegido.primera) { elegido.inicio = t; elegido.primera = false; }
t += duracion;
elegido.restante -= duracion;
if (elegido.restante <= 0) {
elegido.fin = t;
elegido.respuesta = elegido.inicio - elegido.llegada;
elegido.retorno = elegido.fin - elegido.llegada;
elegido.espera = elegido.retorno - elegido.rafaga;
ejecutados.push(elegido);
restantes = restantes.filter(function (x) { return x !== elegido; });
}
}
return ejecutados;
}
5. Prioridades con envejecimiento
La versión con envejecimiento aumenta la prioridad de los procesos que esperan. La prioridad efectiva es prioridad_base + tiempo_esperado × velocidad:
function prioridadesAging(procesos, velEnvejecimiento) {
var p = procesos.map(function (x) {
return { pid: x.pid, llegada: x.llegada, rafaga: x.rafaga,
prioridadBase: x.prioridad, prioridad: x.prioridad,
inicio: 0, fin: 0, respuesta: 0, espera: 0, retorno: 0,
tiempoEspera: 0 };
});
var t = 0;
var ejecutados = [];
var restantes = p.slice();
while (ejecutados.length < p.length) {
/* Actualizar prioridades por envejecimiento. */
restantes.forEach(function (x) {
x.prioridad = x.prioridadBase + x.tiempoEspera * velEnvejecimiento;
});
var disponibles = restantes.filter(function (x) { return x.llegada <= t; });
if (disponibles.length === 0) {
var sig = restantes.reduce(function (a, b) {
return a.llegada < b.llegada ? a : b;
});
var dt = sig.llegada - t;
restantes.forEach(function (x) {
if (x.llegada <= t) x.tiempoEspera += dt;
});
t = sig.llegada;
disponibles = restantes.filter(function (x) { return x.llegada <= t; });
}
var elegido = disponibles.reduce(function (a, b) {
if (a.prioridad !== b.prioridad) return a.prioridad > b.prioridad ? a : b;
if (a.llegada !== b.llegada) return a.llegada < b.llegada ? a : b;
return a.pid < b.pid ? a : b;
});
var dt2 = (t < elegido.llegada ? elegido.llegada - t : 0) + elegido.rafaga;
/* Actualizar tiempos de espera de los que no se ejecutan. */
restantes.forEach(function (x) {
if (x !== elegido && x.llegada <= t) x.tiempoEspera += elegido.rafaga;
});
restantes = restantes.filter(function (x) { return x !== elegido; });
if (t < elegido.llegada) t = elegido.llegada;
elegido.inicio = t;
elegido.fin = t + elegido.rafaga;
t = elegido.fin;
elegido.respuesta = elegido.inicio - elegido.llegada;
elegido.retorno = elegido.fin - elegido.llegada;
elegido.espera = elegido.retorno - elegido.rafaga;
ejecutados.push(elegido);
}
return ejecutados;
}
6. Gantt para cada variante
Las tres variantes son no apropiativas (excepto la apropiativa), así que cada proceso se ejecuta de una sola vez. El Gantt se dibuja igual que en FCFS:
function dibujarGantt(procesos, ganttEl, ticksEl) {
ganttEl.innerHTML = '';
ticksEl.innerHTML = '';
if (procesos.length === 0) return;
var total = procesos[procesos.length - 1].fin;
var t = 0;
procesos.forEach(function (p, idx) {
if (p.inicio > t) {
var hueco = document.createElement('span');
hueco.style.flex = (p.inicio - t) + ' 1 0';
hueco.textContent = 'ociosa';
hueco.style.background = 'var(--surface-2)';
hueco.style.color = 'var(--ink-soft)';
ganttEl.appendChild(hueco);
}
var span = document.createElement('span');
span.className = 'p' + (idx % 3);
span.style.flex = p.rafaga + ' 1 0';
span.textContent = 'P' + p.pid;
ganttEl.appendChild(span);
var marca = document.createElement('span');
marca.style.left = (p.inicio / total * 100) + '%';
marca.textContent = p.inicio;
ticksEl.appendChild(marca);
t = p.fin;
});
var fin = document.createElement('span');
fin.style.left = '100%';
fin.textContent = total;
ticksEl.appendChild(fin);
}
7. Tabla comparativa
La tabla muestra las métricas de las tres variantes para la misma carga:
function promedios(procesos) {
var n = procesos.length;
var sr = 0, se = 0, st = 0;
for (var i = 0; i < n; i++) {
sr += procesos[i].respuesta;
se += procesos[i].espera;
st += procesos[i].retorno;
}
return { respuesta: sr / n, espera: se / n, retorno: st / n };
}
function renderComparativa(r1, r2, r3, el) {
var p1 = promedios(r1), p2 = promedios(r2), p3 = promedios(r3);
el.innerHTML = '';
var filas = [
['Tiempo de respuesta promedio', p1.respuesta, p2.respuesta, p3.respuesta],
['Tiempo de espera promedio', p1.espera, p2.espera, p3.espera],
['Tiempo de retorno promedio', p1.retorno, p2.retorno, p3.retorno]
];
filas.forEach(function (f) {
var tr = document.createElement('tr');
var td0 = document.createElement('td'); td0.textContent = f[0]; tr.appendChild(td0);
var td1 = document.createElement('td'); td1.textContent = f[1].toFixed(2); tr.appendChild(td1);
var td2 = document.createElement('td'); td2.textContent = f[2].toFixed(2); tr.appendChild(td2);
var td3 = document.createElement('td'); td3.textContent = f[3].toFixed(2); tr.appendChild(td3);
el.appendChild(tr);
});
}
8. Los controles
Los controles son los mismos que en los simuladores anteriores, más un control para la velocidad de envejecimiento:
- Procesos: número de procesos (2 a 8).
- Llegada máxima: llegada máxima posible.
- Ráfaga máxima: ráfaga máxima posible.
- Semilla: semilla del generador pseudoaleatorio.
- Velocidad de envejecimiento: cuánto aumenta la prioridad por unidad de tiempo (0 = sin envejecimiento).
9. Simulador interactivo
Este simulador ejecuta las tres variantes sobre la misma carga y muestra los Gantt y las métricas:
Simulador · prioridades (3 variantes)
No apropiativa
| Proceso | Llegada | Ráfaga | Prioridad | Inicio | Fin | Respuesta | Espera | Retorno |
|---|---|---|---|---|---|---|---|---|
Apropiativa
| Proceso | Llegada | Ráfaga | Prioridad | Inicio | Fin | Respuesta | Espera | Retorno |
|---|---|---|---|---|---|---|---|---|
Con envejecimiento
| Proceso | Llegada | Ráfaga | Prioridad | Inicio | Fin | Respuesta | Espera | Retorno |
|---|---|---|---|---|---|---|---|---|
Comparativa
| Métrica | No apropiativa | Apropiativa | Con envejecimiento |
|---|
10. Inanición en el simulador
La inanición es visible en el simulador: un proceso de baja prioridad espera indefinidamente si siempre hay procesos de mayor prioridad. Con el envejecimiento, la prioridad del proceso aumenta hasta que se ejecuta.
Para ver la inanición en acción, genera una carga con un proceso de prioridad 1 y varios de prioridad 5. Sin envejecimiento (velocidad 0), el proceso de prioridad 1 espera hasta el final. Con envejecimiento (velocidad > 0), se ejecuta antes.
11. Verificación y casos de prueba
| Caso | Entrada | Resultado esperado |
|---|---|---|
| Todos en 0 | P1(0,5,prio3), P2(0,3,prio5), P3(0,2,prio1) | No apropiativa: P2, P1, P3. Apropiativa: igual (todos en 0). Envejecimiento: igual. |
| Llegadas distintas | P1(0,8,prio2), P2(1,4,prio5), P3(2,2,prio1) | Apropiativa interrumpe P1 cuando llega P2. No apropiativa no. |
| Inanición | P1(0,20,prio1), P2..Pn(1..n,1,prio5) | Sin envejecimiento: P1 espera hasta el final. Con envejecimiento: P1 se ejecuta antes. |
| Un proceso | P1(0,5,prio3) | Todas las variantes: un bloque 0-5. |
12. Resumen
- El simulador ejecuta tres variantes sobre la misma carga: no apropiativa, apropiativa y con envejecimiento.
- La no apropiativa no interrumpe al proceso en curso.
- La apropiativa interrumpe si llega un proceso de mayor prioridad.
- El envejecimiento aumenta la prioridad de los procesos que esperan.
- La tabla comparativa muestra las métricas de las tres variantes lado a lado.
- La inanición es visible: un proceso de baja prioridad espera indefinidamente sin envejecimiento.
- Con envejecimiento, el proceso de baja prioridad se ejecuta antes.
13. Preguntas de repaso
Intenta responder cada una antes de abrir la respuesta.
1. ¿Qué tres variantes compara el simulador?
Prioridades no apropiativas, apropiativas y con envejecimiento.
2. ¿En qué se diferencia la apropiativa de la no apropiativa?
La apropiativa interrumpe al proceso en curso si llega uno de mayor prioridad.
3. ¿Cómo se implementa el envejecimiento en el simulador?
La prioridad efectiva es prioridad_base + tiempo_esperado × velocidad.
4. ¿Qué ocurre sin envejecimiento cuando un proceso de baja prioridad espera?
Espera indefinidamente si siempre hay procesos de mayor prioridad.
5. ¿Qué ocurre con envejecimiento?
La prioridad del proceso aumenta hasta que supera a los nuevos y se ejecuta.
6. ¿Por qué la carga debe ser la misma para las tres variantes?
Para que la comparación sea válida: la diferencia se debe solo al algoritmo.
7. ¿Qué métrica mejora más la apropiativa respecto a la no apropiativa?
El tiempo de respuesta: un proceso de alta prioridad se ejecuta inmediatamente.
8. ¿Qué compromiso introduce el envejecimiento?
Rompe la optimalidad: un proceso de baja prioridad puede ejecutarse antes que uno de alta si ha esperado suficiente.
9. ¿Cómo se desempatan procesos con la misma prioridad?
Por llegada, luego por pid.
10. ¿Qué control permite ajustar el envejecimiento?
El control «Envejecimiento» (velocidad). Con 0 se desactiva.
Para practicar
- Ejecuta el simulador con envejecimiento 0 y observa la inanición.
- Aumenta el envejecimiento a 2 y observa cómo el proceso de baja prioridad se ejecuta.
- Compara las métricas de las tres variantes para la misma carga.
- Genera un caso con inanición y verifica que el envejecimiento la evita.
- Modifica el simulador para añadir desempate por SJF en lugar de FCFS.
- Implementa la versión apropiativa con envejecimiento.