← Planificación de la CPU Bloque 5 · Planificación por prioridades · Tema 19 de 44

Tema 19

Simulador por prioridades

En los temas 17 y 18 viste la planificación por prioridades y el envejecimiento por separado. Este tema los reúne en un simulador interactivo que ejecuta tres variantes sobre la misma carga: prioridades no apropiativas, prioridades apropiativas y prioridades con envejecimiento. Así se ve directamente el efecto de la apropiación y del envejecimiento en el Gantt y en las métricas.

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.

Misma carga, tres variantes

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:

ParteResponsabilidadFunciones
ModeloRepresentar un proceso con prioridad.crearProceso, copiar
GeneradorProducir cargas reproducibles con prioridades.crearGenerador, generarCarga
No apropiativaPrioridades sin interrupción.prioridadesNoAprop
ApropiativaPrioridades con interrupción.prioridadesAprop
Con envejecimientoPrioridades con envejecimiento.prioridadesAging
VistaDibujar Gantt y tabla para cada variante.dibujarGantt, renderTabla
ControladorLeer 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:

prioridadesNoAprop.js
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:

prioridadesAprop.js
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:

prioridadesAging.js
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:

dibujarGantt.js
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:

renderComparativa.js
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:

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

ProcesoLlegadaRáfagaPrioridadInicioFinRespuestaEsperaRetorno

Apropiativa

ProcesoLlegadaRáfagaPrioridadInicioFinRespuestaEsperaRetorno

Con envejecimiento

ProcesoLlegadaRáfagaPrioridadInicioFinRespuestaEsperaRetorno

Comparativa

MétricaNo apropiativaApropiativaCon 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

CasoEntradaResultado esperado
Todos en 0P1(0,5,prio3), P2(0,3,prio5), P3(0,2,prio1)No apropiativa: P2, P1, P3. Apropiativa: igual (todos en 0). Envejecimiento: igual.
Llegadas distintasP1(0,8,prio2), P2(1,4,prio5), P3(2,2,prio1)Apropiativa interrumpe P1 cuando llega P2. No apropiativa no.
InaniciónP1(0,20,prio1), P2..Pn(1..n,1,prio5)Sin envejecimiento: P1 espera hasta el final. Con envejecimiento: P1 se ejecuta antes.
Un procesoP1(0,5,prio3)Todas las variantes: un bloque 0-5.

12. Resumen

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

  1. Ejecuta el simulador con envejecimiento 0 y observa la inanición.
  2. Aumenta el envejecimiento a 2 y observa cómo el proceso de baja prioridad se ejecuta.
  3. Compara las métricas de las tres variantes para la misma carga.
  4. Genera un caso con inanición y verifica que el envejecimiento la evita.
  5. Modifica el simulador para añadir desempate por SJF en lugar de FCFS.
  6. Implementa la versión apropiativa con envejecimiento.