feat: add MLFQ scheduling algorithm and thread scheduler

- Implemented MLFQ (Multilevel Feedback Queue) algorithm with dynamic priority levels and quantum management.
- Created unit tests for MLFQ algorithm covering initialization, demotion, selection, priority boost, preemption, and quantum expiration.
- Developed a ThreadScheduler class supporting FCFS, RR, and PRIORITY scheduling policies for intra-process thread management.
- Added unit tests for ThreadScheduler to validate thread selection and quantum expiration logic.
This commit is contained in:
Lucas Guerra committed 2026-03-24 23:09:16 -03:00
1 parent 532c14e382
commit 5761dfe70a
34 files changed
+3789 -334

No files matched your search

+101 -1
View File
@@ -147,7 +147,7 @@ The process with the **highest ratio** is selected. This naturally increases the
## Multilevel Queue
**Type:** Preemptive
**Type:** Preemptive
**Parameters:** number of queues, quantum per queue
### How it works
@@ -158,6 +158,106 @@ This allows different classes of processes to be treated distinctly: for example
---
## MLFQ (Multilevel Feedback Queue)
**Type:** Preemptive
**Parameters:** number of queues, quantum per queue (default: [2, 4, 8]), boost interval
### How it works
MLFQ is an extension of the Multilevel Queue that allows **processes to move between queues** based on their observed runtime behavior — eliminating the need to know burst durations in advance.
#### Movement rules
| Event | Action |
|-------|--------|
| Newly arrived process | Enters **queue 0** (highest priority) |
| Process exhausts the quantum of its current queue | **Demoted** to the next queue (lower priority) |
| Process returns from I/O | Stays in the **same queue** (cooperative behavior is rewarded) |
| Boost tick (configurable interval) | **All** processes are moved to queue 0 |
#### Why is MLFQ efficient?
- Interactive processes (I/O Bound) naturally stay in high-priority queues because they leave the CPU before exhausting the quantum.
- Batch processes (CPU Bound) progressively migrate to lower-priority queues where they receive larger quantums.
- The periodic boost prevents starvation: even processes that fell to lower queues eventually return to the top.
#### Example with 3 queues (quantums: 2, 4, 8)
```
Process A (CPU Bound, burst=12):
Queue 0 → uses 2 ticks, demoted → Queue 1
Queue 1 → uses 4 ticks, demoted → Queue 2
Queue 2 → uses remaining 6 ticks (quantum=8, not exhausted)
Process B (I/O Bound, burst=1 CPU + I/O + burst=1 CPU):
Queue 0 → uses 1 tick → goes to I/O → returns to Queue 0
→ always stays in the highest-priority queue
```
### Parameters
| Parameter | Default | Description |
|-----------|---------|-------------|
| Number of queues | 3 | Number of priority queues |
| Quantum per queue | [2, 4, 8] | Time slice for each queue |
| Boost interval | 20 ticks | Every N ticks, all processes move up to queue 0 |
---
## Thread Models
PSS supports **thread** simulation within processes, implementing the three classical models described in Operating Systems textbooks.
### General Concepts
- A process can have **N user threads**, each with its own burst sequence (CPU and I/O).
- The scheduling policy among threads within the same process is configurable: **FCFS**, **Round Robin**, or **Priority**.
- The thread model determines how user threads map to kernel threads.
### MANY_TO_ONE (N:1)
**N user threads → 1 kernel thread**
- Only one thread can use the CPU at a time within the process.
- If any thread initiates I/O, **the entire process blocks** (the kernel thread is stuck).
- No real parallelism; very simple to implement.
- Simulated by the `many_to_one` presets.
### ONE_TO_ONE (1:1)
**Each user thread → 1 dedicated kernel thread**
- Threads block **independently**: if Thread A does I/O, Threads B and C remain in the Ready queue.
- The process only terminates when **all** threads have terminated.
- The process blocks only when all threads are simultaneously in I/O.
- Simulated by the `one_to_one` presets.
### MANY_TO_MANY (N:M)
**N user threads → M kernel threads (M ≤ N)**
- The number of kernel threads (`kernelThreadCount`) is configurable.
- If all M kernel slots are occupied, additional user threads wait in the **kernel wait queue** (`KernelWait`).
- Combines flexibility with resource control: more active threads than N:1 but less overhead than 1:1.
- Simulated by the `many_to_many` presets.
### Intra-Process Policy
Defines how the scheduler selects the next thread when multiple threads are Ready within the same process:
| Policy | Behavior |
|--------|----------|
| **FCFS** | The oldest thread in the Ready queue is selected |
| **Round Robin (RR)** | Each thread receives `threadQuantum` ticks; when exhausted, it goes to the end of the queue |
| **Priority** | The highest-priority thread is selected; immediate preemption if a higher-priority thread becomes ready |
### Gantt Chart with Threads
When a process has threads, the Gantt chart displays **sub-rows** below the process row, one per thread, with colors derived from the process color (40° hue shift per thread). The state of each thread (New, Ready, Running, Waiting, Terminated) is colored using the same CSS variables as process states.
---
## References
- Silberschatz, A., Galvin, P. B., & Gagne, G., *Operating System Concepts*, 10th edition.
+101 -1
View File
@@ -147,7 +147,7 @@ O processo com o **maior ratio** é selecionado. Isso faz com que processos que
## Multilevel Queue
**Tipo:** Preemptivo
**Tipo:** Preemptivo
**Parâmetros:** número de filas, quantum por fila
### Como funciona
@@ -158,6 +158,106 @@ Isso permite tratar diferentes classes de processos de forma distinta: por exemp
---
## MLFQ (Multilevel Feedback Queue)
**Tipo:** Preemptivo
**Parâmetros:** número de filas, quantum por fila (padrão: [2, 4, 8]), intervalo de boost
### Como funciona
O MLFQ é uma extensão do Multilevel Queue que permite **mover processos entre filas** com base em seu comportamento observado em tempo de execução — eliminando a necessidade de conhecer os bursts antecipadamente.
#### Regras de movimentação
| Evento | Ação |
|--------|------|
| Processo recém-chegado | Entra na **fila 0** (maior prioridade) |
| Processo esgota o quantum da fila atual | **Rebaixado** para a fila seguinte (menor prioridade) |
| Processo retorna do I/O | Permanece na **mesma fila** (comportamento cooperativo é recompensado) |
| Tick de boost (intervalo configurável) | **Todos** os processos são movidos para a fila 0 |
#### Por que o MLFQ é eficiente?
- Processos interativos (I/O Bound) naturalmente ficam nas filas de alta prioridade porque abandonam a CPU antes de esgotar o quantum.
- Processos batch (CPU Bound) migram progressivamente para filas de baixa prioridade onde recebem quantuns maiores.
- O boost periódico evita starvation: mesmo processos que caíram para as filas inferiores eventual­mente retornam ao topo.
#### Exemplo com 3 filas (quantuns: 2, 4, 8)
```
Processo A (CPU Bound, burst=12):
Fila 0 → usa 2 ticks, rebaixado → Fila 1
Fila 1 → usa 4 ticks, rebaixado → Fila 2
Fila 2 → usa 6 ticks restantes (quantum=8, não esgota)
Processo B (I/O Bound, burst=1 CPU + I/O + burst=1 CPU):
Fila 0 → usa 1 tick → vai para I/O → retorna à Fila 0
→ sempre permanece na fila de maior prioridade
```
### Parâmetros
| Parâmetro | Padrão | Descrição |
|-----------|--------|-----------|
| Número de filas | 3 | Quantidade de filas de prioridade |
| Quantuns por fila | [2, 4, 8] | Fatia de tempo de cada fila |
| Intervalo de boost | 20 ticks | A cada N ticks, todos os processos sobem para a fila 0 |
---
## Modelos de Threads
O PSS suporta simulação de **threads** dentro de processos, implementando os três modelos clássicos descritos em Sistemas Operacionais.
### Conceitos Gerais
- Um processo pode ter **N threads de usuário**, cada uma com sua própria sequência de bursts (CPU e I/O).
- A política de escalonamento entre threads de um mesmo processo é configurável: **FCFS**, **Round Robin** ou **Prioridade**.
- O modelo de thread determina como threads de usuário se mapeiam para threads de kernel.
### MANY_TO_ONE (N:1)
**N threads de usuário → 1 thread de kernel**
- Apenas uma thread pode usar a CPU por vez dentro do processo.
- Se qualquer thread iniciar I/O, **todo o processo bloqueia** (a thread de kernel fica presa).
- Sem paralelismo real; muito simples de implementar.
- Simulado pelos presets `many_to_one`.
### ONE_TO_ONE (1:1)
**Cada thread de usuário → 1 thread de kernel dedicada**
- Threads bloqueiam **independentemente**: se a Thread A faz I/O, as Threads B e C continuam na fila Ready.
- Processo só termina quando **todas** as threads terminam.
- Processo bloqueia somente quando todas as threads estão simultâneamente em I/O.
- Simulado pelos presets `one_to_one`.
### MANY_TO_MANY (N:M)
**N threads de usuário → M threads de kernel (M ≤ N)**
- O número de threads de kernel (`kernelThreadCount`) é configurável.
- Se todas as M slots de kernel estiverem ocupadas, threads de usuário adicionais ficam em **fila de espera de kernel** (`KernelWait`).
- Combina flexibilidade com controle de recursos: mais threads ativas que N:1 mas menos overhead que 1:1.
- Simulado pelos presets `many_to_many`.
### Política Intra-Processo
Define como o escalonador seleciona a próxima thread quando há múltiplas threads Ready no mesmo processo:
| Política | Comportamento |
|----------|--------------|
| **FCFS** | Thread mais antiga na fila Ready é selecionada |
| **Round Robin (RR)** | Cada thread recebe `threadQuantum` ticks; ao esgotar, vai para o fim da fila |
| **Priority** | Thread de maior prioridade é selecionada; preempção imediata se nova thread de maior prioridade chegar |
### Diagrama de Gantt com Threads
Quando um processo possui threads, o Gantt exibe **sub-linhas** abaixo da linha do processo, uma por thread, com cores derivadas da cor do processo (deslocamento de matiz de 40° por thread). O estado de cada thread (New, Ready, Running, Waiting, Terminated) é colorido com as mesmas variáveis CSS dos processos.
---
## Referências
- Silberschatz, A., Galvin, P. B., & Gagne, G., *Operating System Concepts*, 10ª edição.
+35
View File
@@ -105,6 +105,41 @@ The total height of each bar equals the **Turnaround Time** of the process.
---
## Thread Metrics
When a process has threads, PSS computes individual metrics for each thread. These metrics are displayed in the **Threads** tab of the metrics panel (visible only when there are processes with threads).
### Per-Thread Metrics
| Metric | Formula | What it means |
|--------|---------|----------------|
| **Arrival Time** | `arrivalTick` | Tick at which the thread first entered the Ready queue |
| **Start** | `startTick` | Tick at which the thread first used the CPU |
| **Finish** | `finishTick` | Tick at which the thread completed all of its bursts |
| **Response Time** | `startTick − arrivalTick` | How long the thread waited before receiving the CPU for the first time |
| **Turnaround Time** | `finishTick − arrivalTick` | Total lifetime of the thread (arrival → completion) |
| **Waiting Time** | `turnaround − cpuTime − ioTime` | Time in the Ready queue without doing anything useful |
| **CPU Time** | Σ ticks in Running state | Total CPU consumed by the thread |
| **I/O Time** | Σ ticks in Waiting state | Total time waiting for I/O |
### Thread Models and their impact on metrics
The thread model directly affects the recorded times:
| Model | Impact on Waiting Time |
|-------|------------------------|
| **MANY_TO_ONE** | Waiting Time tends to be high: any thread's I/O blocks the entire process, forcing other threads to wait |
| **ONE_TO_ONE** | Lower Waiting Time: threads block independently; while one does I/O, the others remain in the Ready queue |
| **MANY_TO_MANY** | Intermediate Waiting Time: threads that exceed the kernel slots enter `KernelWait` (shown with reduced opacity in the Gantt chart) |
### Visualization
The thread table displays inline horizontal mini progress bars in the Response, Turnaround, Waiting, CPU, and I/O columns, normalized by the largest value in each column across all threads — just like the process metrics table.
Each thread has a color derived from the parent process color (40° HSL hue shift per thread), shown as a colored dot in the table and as a sub-row in the Gantt chart.
---
## Example
Consider three processes with the following bursts:
+35
View File
@@ -105,6 +105,41 @@ A altura total de cada barra equivale ao **Turnaround Time** do processo.
---
## Métricas de Threads
Quando um processo possui threads, o PSS calcula métricas individuais para cada thread. Essas métricas são exibidas na aba **Threads** do painel de métricas (visível apenas quando há processos com threads).
### Métricas por Thread
| Métrica | Fórmula | O que significa |
|---------|---------|----------------|
| **Arrival Time** | `arrivalTick` | Tick em que a thread entrou pela primeira vez na fila Ready |
| **Start** | `startTick` | Tick em que a thread usou a CPU pela primeira vez |
| **Finish** | `finishTick` | Tick em que a thread encerrou todos os seus bursts |
| **Response Time** | `startTick − arrivalTick` | Quanto a thread esperou antes de receber a CPU pela primeira vez |
| **Turnaround Time** | `finishTick − arrivalTick` | Tempo total de vida da thread (chegada → término) |
| **Waiting Time** | `turnaround − cpuTime − ioTime` | Tempo na fila Ready sem executar nada útil |
| **CPU Time** | Σ ticks em estado Running | Total de CPU consumido pela thread |
| **I/O Time** | Σ ticks em estado Waiting | Total de tempo aguardando I/O |
### Modelos de Thread e seu impacto nas métricas
O modelo de thread afeta diretamente os tempos registrados:
| Modelo | Impacto no Waiting Time |
|--------|------------------------|
| **MANY_TO_ONE** | Waiting Time tende a ser alto: qualquer I/O de uma thread bloqueia todo o processo, forçando as demais threads a esperarem |
| **ONE_TO_ONE** | Waiting Time menor: threads bloqueiam independentemente; enquanto uma faz I/O, as outras continuam na fila Ready |
| **MANY_TO_MANY** | Waiting Time intermediário: threads que excedem os slots de kernel ficam em `KernelWait` (indicadas com `opacity` reduzida no Gantt) |
### Visualização
A tabela de threads exibe mini barras de progresso horizontais nas colunas de Response, Turnaround, Waiting, CPU e I/O, normalizadas pelo maior valor de cada coluna entre todas as threads — assim como na tabela de processos.
Cada thread possui uma cor derivada da cor do processo pai (deslocamento de matiz HSL de 40° por thread), exibida como ponto colorido na tabela e como sub-linha no Gantt.
---
## Exemplo
Considere três processos com os seguintes bursts: