Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiqueue scheduler & prioriry impl #215

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
22af7a5
feat: defining prempition clock to 5
JoaoVitorSD May 1, 2024
08f83e0
feat: proc priority & fork initialize process with medium priority
JoaoVitorSD May 1, 2024
775c09a
feat: couting ticks for schedulling
JoaoVitorSD May 4, 2024
772d54a
fix: comment stdlib lines
JoaoVitorSD May 7, 2024
61ba8df
feat: prioriry manager in proc.c
JoaoVitorSD May 9, 2024
3441a62
update: adição da implementação inicial da medium queue
MP022 May 10, 2024
16d03f0
update: get_next_proc foi atualizado mas não foi testado
MP022 May 10, 2024
737da36
feat: not prempting process if current process running is realtime pr…
JoaoVitorSD May 10, 2024
ba648a6
feat: next proc low queue
JoaoVitorSD May 10, 2024
dba0e15
feat: increasing runnable time process and cfs based shceduler
JoaoVitorSD May 10, 2024
df6e0ca
doc: initial doc
JoaoVitorSD May 10, 2024
4fdd31b
fix: correção da regra de escolha de processo da fila low
MP022 May 10, 2024
b893c86
update: implementação da politica de escalonamento da fila media
MP022 May 10, 2024
126e01e
feat: queues impl & promoting process prioriry based on retime
JoaoVitorSD May 12, 2024
bf5547e
refac: renaming struc proc fields & aging method
JoaoVitorSD May 12, 2024
0b6a694
feat: aging process
JoaoVitorSD May 13, 2024
91a2ce0
update: adicionando a fila medium novamente
MP022 May 13, 2024
f0648b5
Merge branch 'mp' into master
MP022 May 13, 2024
6a29a07
fix: a alteração da verificação se o processo é runnable e se está na…
MP022 May 13, 2024
6eca667
fix: rutime increment
JoaoVitorSD May 13, 2024
45b1257
uptade: ativando a medium priority
MP022 May 13, 2024
624cbfd
feat: prempting process in all queues
JoaoVitorSD May 13, 2024
b60b4ce
fix: um erro foi mandito com o ultimo commit
MP022 May 13, 2024
2296124
update: reordenadno as politicas de escalonamento nos quatro niveis
MP022 May 13, 2024
660fc55
fix: a plocitica de escalonamento do fcfs voltou a ser fcfs
MP022 May 13, 2024
4482ddd
doc & refac: improve readme e high priority scheduller
JoaoVitorSD May 13, 2024
372a1ac
doc: coauthor name
JoaoVitorSD May 13, 2024
9297987
doc: readme in md
JoaoVitorSD May 13, 2024
00c46b6
doc: readme in md
JoaoVitorSD May 13, 2024
e386450
feat: sanity 1.0
JoaoVitorSD May 14, 2024
63c0dfb
feat: process type in sanity
JoaoVitorSD May 14, 2024
9506a01
update: terminando de imlementar o sanity.c basead no exemplo do test…
MP022 May 14, 2024
9fa5906
feat: process sys call analysis impl
JoaoVitorSD May 14, 2024
9361861
feat: yield return system call
JoaoVitorSD May 14, 2024
bc7b528
feat: calc avg values
JoaoVitorSD May 14, 2024
3b88b22
refac: renaming redudant statements
JoaoVitorSD May 14, 2024
d596bb1
fix: process forking awaiting
JoaoVitorSD May 15, 2024
cdfd910
feat: output txt analysis
JoaoVitorSD May 15, 2024
3fbf845
doc: atualização da documentação
MP022 May 15, 2024
6790bdf
Co-authored-by: Marcos Paulo F. de Souza <[email protected].…
JoaoVitorSD May 15, 2024
359addf
doc: improve doc
JoaoVitorSD May 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ kernel
kernelmemfs
mkfs
.gdbinit


output.txt
33 changes: 33 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"files.associations": {
"spinlock.h": "c",
"mmu.h": "c",
"proc.h": "c",
"user.h": "c",
"realtime_queue.h": "c",
"list.h": "c",
"stdlib.h": "c",
"types.h": "c",
"high_queue.h": "c",
"low_queue.h": "c",
"fcntl.h": "c",
"list": "c",
"array": "c",
"string_view": "c",
"initializer_list": "c",
"utility": "c",
"medium_queue.h": "c",
"defs.h": "c",
"memlayout.h": "c",
"x86.h": "c",
"param.h": "c",
"stat.h": "c",
"syscall.h": "c",
"traps.h": "c",
"fs.h": "c",
"compare": "c",
"functional": "c",
"tuple": "c",
"type_traits": "c"
}
}
18 changes: 12 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ OBJS = \
uart.o\
vectors.o\
vm.o\
list.o\

# Cross-compiling (e.g., on Mac OS X)
# TOOLPREFIX = i386-jos-elf
Expand All @@ -51,7 +52,7 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d
endif

# If the makefile can't find QEMU, specify its path here
# QEMU = qemu-system-i386
# QEMU = qemu-system-x86_64

# Try to infer the correct QEMU
ifndef QEMU
Expand Down Expand Up @@ -156,6 +157,7 @@ _forktest: forktest.o $(ULIB)
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o
$(OBJDUMP) -S _forktest > forktest.asm


mkfs: mkfs.c fs.h
gcc -Werror -Wall -o mkfs mkfs.c

Expand All @@ -179,11 +181,12 @@ UPROGS=\
_sh\
_stressfs\
_usertests\
_sanity\
_wc\
_zombie\

fs.img: mkfs README $(UPROGS)
./mkfs fs.img README $(UPROGS)
fs.img: mkfs $(UPROGS)
./mkfs fs.img $(UPROGS)

-include *.d

Expand All @@ -196,7 +199,7 @@ clean:

# make a printout
FILES = $(shell grep -v '^\#' runoff.list)
PRINT = runoff.list runoff.spec README toc.hdr toc.ftr $(FILES)
PRINT = runoff.list runoff.spec toc.hdr toc.ftr $(FILES)

xv6.pdf: $(PRINT)
./runoff
Expand All @@ -223,6 +226,9 @@ QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,

qemu: fs.img xv6.img
$(QEMU) -serial mon:stdio $(QEMUOPTS)

qemu-output: fs.img xv6.img
$(QEMU) -serial mon:stdio $(QEMUOPTS) > output.txt

qemu-memfs: xv6memfs.img
$(QEMU) -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp $(CPUS) -m 256
Expand Down Expand Up @@ -250,8 +256,8 @@ qemu-nox-gdb: fs.img xv6.img .gdbinit
EXTRA=\
mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\
ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c\
printf.c umalloc.c\
README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\
printf.c umalloc.c sanity.c\
dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\
.gdbinit.tmpl gdbutil\

dist:
Expand Down
51 changes: 0 additions & 51 deletions README

This file was deleted.

135 changes: 135 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# TP Sistemas Operacionais XV6
## Authors:
Marcos Paulo Ferreira de Souza: https://github.com/MP022
João Vítor Santana Depollo: https://github.com/@JoaoVitorSD
### 1. Introdução
O objetivo deste trabalho é implementar alterações na política de escalonamento do sistema operacional xv6. A maior parte das alterações foram feitas pelas chamadas do scheduler e na inicialização do proc, na função `allocproc()`.
### Scheduller
A prempção do scheduler ocorre com a alteração de um loop que na sua nova versão incrementa uma variável `prempt`, quando atinge o valor da variável `INTERV`, a variável `prempt` tem o seu valor zerado e a função `premptProcess()` é chamada. Através dessa função diferentes políticas de escalonamento são aplicadas para escolher o próximo processo a ser executado de acordo com a sua prioridade.
## Prioridades
Foram definidas 4 níveis de prioridades, sendo eles:
- Low
- Medium
- High
- Realtime
A implementação foi feita via enum, no arquivo `proc.h`:
```c
enum procpriority
{
LOW,
MEDIUM,
HIGH,
REALTIME
};
```
## Escalonamento
Cada prioridade possui uma política de escalonamento diferente, e para evitar inanição também foi implementada uma função que aumenta a prioridade dos processos que ficarem X titcks sem serem executados. Neste contexto, foram implementadas as seguintes políticas de escalonamento:
### Realtime
Utiliza o Round Robin, a ideia é que o processo que está a mais tempo sem ser escolhido para ser executado, seja o primeiro a ser executado. Os processos serão executados com um tempo fixo, e depois serão colocados no final da fila novamente. Para esta implementação, é utilizado o valor `last_cycle` do processo, que é incrementado a cada ciclo em que o processo é escolhido para ser executado. Dessa forma, o round_robin vai buscar o processo com o menor `last_cycle` para ser executado.
### High
Baseado no CFS (Completely Fair Scheduler), a ideia é que o processo que tem a menor quantidade de tempo de CPU, seja o próximo a ser executado. Para isso, é utilizado o valor `rutime` do processo, que é incrementado a cada tick que o processo for escolhido para ser executado. Dessa forma, o CFS vai buscar o processo com o menor `rutime` para ser executado. Essa política não gera inanição e dará maior prioridade para os processos que tem menor tempo de CPU, (io-bound).
### Medium
Similar a política do Round Robin, porém essa considera a quantidade de vezes que o processo foi escolhido para ser executado. A ideia é que o processo que foi executado menos vezes, seja o próximo a ser executado. Dessa forma, se um processo p2 chegar após o processo p1, mas o processo p1 já foi executado 3 vezes, e o processo p2 ainda não foi executado, o processo p2 será executado antes do processo p1 por 3 vezes. Dessa forma, é possivel que aconteça uma inação com os processos mais antigos justamente por já terem sido escolhidos diversas vezes para serem executados.
### Low
Utiliza o FCFS (First Come, First Served), utilizando o valor `ctime` do processo para identificar qual o processo mais antigo. A ideia é que o processo que chegou primeiro, seja o primeiro a ser executado. Com essa política de escalonamento a inação é um problema. No caso do processo mais velho não terminar de executar nunca, já que nessa política o processo só para de executar quando ele termina, os outros processos não serão executados. Mas essa solução funciona bem para processos que ficam mais tempo na cpu (cpu-bound).
## Justificativa
### Realtime
Ser o mais justo possível com os processos, garantindo que todos terão a mesma prioridade,independente do tempo que o processo chegou na cpu, além disso, garantindo a interatividade para processos realtime do sistema.
### High
Priorizar que todos os processos executem a mesma quantidade de tempo, sempre priorizando executar aqueles com menos tempo de execução. Dessa forma garantimos que os processos io-bound, que são processos que passam menos tempo executando e são processos interativos, sejam executados mais rápidamente gerando.
## Medium
Prioriza processos que foram escolhidos poucas ou nenhuma vez. Com esse temos que todos os processos novos seram priozados.
## Low
Pensando em operações de CPU-Bound, essa política garante que os processos que chegaram primeiro sejam executados até o fim. Dessa forma, o `allocproc()` definir a prioridade do processo para `LOW`, permite que os processos de inicialização do sistema sejam executados primeiro. Pois, outros processos criados futuramente, não serão executados até que os processos de inicialização terminem.
# Testes
O arquivo `sanity.c` recebe um parâmetro *n*, executando a função fork *3n* vezes, assim criando *3n* novos processos. Para cada processo criado será executado uma vez a função `run_bound_loop(type)` que executa o loop de acordo com o tipo do processo. São 3 tipos de processo e para cada tipo existem *n* processos.

Para representar os tipos, foi criado o seguinte enum:
```c
enum proctype
{
CPU_BOUND,
S_BOUND,
IO_BOUND
};
```

Para cada processo criado, o processo pai executará a função `wait2`, com o retorno desta chamada, é possível saber qual foi processo que terminou e assim analisar os seus dados de acordo com o seu `proctype`. Após a execução de todos os processos, o programa imprime a média de tempo de sleeping, ready e o turnaround time,calculado pela soma de stime, rutime e retime.


### System Call Implementadas

#### wait2
Recebe 3 endereços de inteiros e atribui os valores de retime, rutime e stime.

#### yield
Para o `proctype` *S_BOUND*, foi necessário implementar a *system call* `yield()`, que faz com que o processo atual seja colocado no final da fila de execução. Permitindo que para cada 20 iterações do loop, o processo chame o yield.

#### Arquivo syscall.h
```c
#define SYS_wait2 22
#define SYS_yield 23
```
## Resultados

### Consideração
>O loop de 0 a 1 milhão, que é executado para os processos do tipo CPU_BOUND e do tipo S_BOUND, possivelmente foi otimizado pelo compilador, o que fez o tempo de execução desses tipos de processo ser muito menor do que deveria, e isso influenciou os resultados obtidos.

> Pra todos os testes, o valor de *n* passado para o programa sanity foi de 20.

> Para analisar o output do processo, o makefile foi alterado, adicionando o comando `make-output` que salva a saída para um arquivo output.txt. Esta saída foi usada como entrada de um programa python no arquivo `sanity_ouput_analyser.py`, para gerar as tabelas.

### Cenários

#### Cenário 1 - Valores Default (Recomendado pelo professor)
- `INTERV = 5`
- `E1TO2 = 200`
- `E2TO3 = 100`
- `E3TO4 = 50`

#### Cenário 2 - Interv 1
Mesmos valores do cenário 1, porém com `INTERV = 1`

## Sleep Time Para Processos IO Bound

#### Cenário 1
![Análise de IO Stime](images/default/stime_iobound_metrics.png)

#### Cenário 2
![Análise de IO Stime](images/low_interv/stime_iobound_metrics.png)

Considerando somente os processos de IO_BOUND, é possível analisar uma propriedade interessante, em que o sleep time vai diminuindo conforme a prioridade do processo aumenta e a quantidade de processos na fila diminui, possivelmente pela menor quantia de trocas de contextos.
Analisando os 2 cenários, foi possível observar que no cenário 1, com um tempo maior de preempção, o primeiro processo de IO_BOUND teve um tempo de sleep menor, possívelmente, por ter tido menos concorrência com os outros processos.
## Ready Time

#### Cenário 1
![Análise de IO Stime](images/default/retime_metrics.png)

#### Cenário 2
![Análise de IO Stime](images/low_interv/retime_metrics.png)

Neste caso, os processos de S_BOUND e CPU_BOUND tiveram um tempo de execução próximos, justamente por serem processos que não fazem I/O. Comparando os 2 cenários, o que possui a preempção menor, atingiu um *stime* menor, porque ao entrarem no state de `RUNNABLE` são atendidos mais rapidamente.

### Rutime

#### Cenário 1
![Análise de IO Stime](images/default/rutime_metrics.png)

#### Cenário 2
![Análise de IO Stime](images/low_interv/rutime_metrics.png)


Como o processo de I/O foi simulado com o `sleep()`, é possível perceber que o processo de IO_BOUND tem um grande trade off pela troca de contextos que ele ocorre, em que levou somente um tick para terminar de executar, mas trocou de contexto 100 vezes, levando 101 ticks de rutime. Em sequência, o S_BOUND, que chama o `yield()` a cada 20 iterações, tem um rutime de 21 ticks, justamente por ter trocado de contexto menor vezes. Por fim, o CPU_BOUND, que não faz I/O, tem um rutime de 1, pois é executado quase que instantaneamente. Dessa forma, em ambos os cenários, temos o mesmo comportamente, mas com mais instabilidade no cenário 2, justamente por ter mais trocas de contexto.
### Sleep Time
#### Cenário 1
![Análise de IO Stime](images/default/stime_metrics.png)

#### Cenário 2
![Análise de IO Stime](images/low_interv/stime_metrics.png)

Como somente o IO_BOUND faz operações de I/O, ele gera uma discrepância nos resultados, em que o sleep time dele é muito maior que os outros processos, justamente por ter que esperar um tempo para que o processo de I/O termine. Por consequência, foi gerado o gráfico de sleep time exclusivo para os processos de IO_BOUND.


## Conclusão

Os resultados foram condizentes com o esperado, em que os processos de IO_BOUND tiveram um tempo de execução maior, justamente por terem que esperar um tempo para que o processo de I/O termine. Em contrapartida, os processos de S_BOUND e CPU_BOUND tiveram um tempo de execução menor. Além disso, foi possível observar que a política de escalonamento implementada foi eficaz, em que os processos de IO_BOUND foram executados de forma justa, sem que um processo monopolizasse a CPU.
1 change: 1 addition & 0 deletions defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ void setproc(struct proc*);
void sleep(void*, struct spinlock*);
void userinit(void);
int wait(void);
int wait2(int*, int*, int*);
void wakeup(void*);
void yield(void);

Expand Down
Loading