diff --git a/.gitignore b/.gitignore index 3e2c9deafe..e5684d589c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ kernel kernelmemfs mkfs .gdbinit + + +output.txt \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..2ff63c8ce9 --- /dev/null +++ b/.vscode/settings.json @@ -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" + } +} diff --git a/Makefile b/Makefile index 09d790cf63..20c88a6ce1 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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: diff --git a/README b/README deleted file mode 100644 index 923e0a4821..0000000000 --- a/README +++ /dev/null @@ -1,51 +0,0 @@ -NOTE: we have stopped maintaining the x86 version of xv6, and switched -our efforts to the RISC-V version -(https://github.com/mit-pdos/xv6-riscv.git) - -xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix -Version 6 (v6). xv6 loosely follows the structure and style of v6, -but is implemented for a modern x86-based multiprocessor using ANSI C. - -ACKNOWLEDGMENTS - -xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer -to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, -2000)). See also https://pdos.csail.mit.edu/6.828/, which -provides pointers to on-line resources for v6. - -xv6 borrows code from the following sources: - JOS (asm.h, elf.h, mmu.h, bootasm.S, ide.c, console.c, and others) - Plan 9 (entryother.S, mp.h, mp.c, lapic.c) - FreeBSD (ioapic.c) - NetBSD (console.c) - -The following people have made contributions: Russ Cox (context switching, -locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin -Clements. - -We are also grateful for the bug reports and patches contributed by Silas -Boyd-Wickizer, Anton Burtsev, Cody Cutler, Mike CAT, Tej Chajed, eyalz800, -Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Filardo, Peter -Froehlich, Yakir Goaron,Shivam Handa, Bryan Henry, Jim Huang, Alexander -Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller, Eddie Kohler, Austin -Liew, Imbar Marinescu, Yandong Mao, Matan Shabtay, Hitoshi Mitake, Carmi -Merimovich, Mark Morrissey, mtasm, Joel Nider, Greg Price, Ayan Shafqat, -Eldar Sehayek, Yongming Shen, Cam Tenny, tyfkda, Rafael Ubal, Warren -Toomey, Stephen Tu, Pablo Ventura, Xi Wang, Keiichi Watanabe, Nicolas -Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang Wei. - -The code in the files that constitute xv6 is -Copyright 2006-2018 Frans Kaashoek, Robert Morris, and Russ Cox. - -ERROR REPORTS - -We don't process error reports (see note on top of this file). - -BUILDING AND RUNNING XV6 - -To build xv6 on an x86 ELF machine (like Linux or FreeBSD), run -"make". On non-x86 or non-ELF machines (like OS X, even on x86), you -will need to install a cross-compiler gcc suite capable of producing -x86 ELF binaries (see https://pdos.csail.mit.edu/6.828/). -Then run "make TOOLPREFIX=i386-jos-elf-". Now install the QEMU PC -simulator and run "make qemu". \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000..bf373b905b --- /dev/null +++ b/README.md @@ -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. \ No newline at end of file diff --git a/defs.h b/defs.h index 82fb982837..b1aaea90a4 100644 --- a/defs.h +++ b/defs.h @@ -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); diff --git a/high_queue.c b/high_queue.c new file mode 100644 index 0000000000..b40e6c52b9 --- /dev/null +++ b/high_queue.c @@ -0,0 +1,112 @@ +#include "high_queue.h" + +int max(int a , int b){ + return a>b? a: b; +} +int min (int a, int b){ + return atree; + struct avl_node *node = tree->root; + if (node == 0) + { + return 0; + } + while (node->left != 0) + { + node = node->left; + } + struct proc *proc = node->proc; + // free(node); + return proc; +} + +int height (struct avl_node * node){ + if(node == 0){ + return 0; + } + return node->height; +} +struct avl_node *rightRotate(avl_node *node){ + avl_node *left = node->left; + avl_node *right = left->right; + left->right = node; + node->left = right; + node->height = max(height(node->left), height(node->right)) + 1; + left->height = max(height(left->left), height(left->right)) + 1; + return left; +} +struct avl_node* leftRotate(avl_node * node) +{ + avl_node *right = node->right; + avl_node *left = right->left; + right->left = node; + node->right = left; + node->height = max(height(node->left), height(node->right)) + 1; + right->height = max(height(right->left), height(right->right)) + 1; + return right; +}; +int getBalance(struct avl_node *node) +{ + if (node == 0) + return 0; + return height(node->left) - height(node->right); +} +struct avl_node *insertRec(avl_node *current, avl_node *new) +{ + if (current == 0) + { + return new; + } + if (new->priority < current->priority) + { + current->left = insertRec(current->left, new); + } + else + { + current->right = insertRec(current->right, new); + } + current->height = 1 + max(height(current->left), height(current->right)); + + int balance = getBalance(current); + if (balance > 1 && new->priority < current->left->priority) + { + return rightRotate(current); + } + if (balance < -1 && new->priority > current->right->priority) + { + return leftRotate(current); + } + if (balance > 1 && new->priority > current->left->priority) + { + current->left = leftRotate(current->left); + return rightRotate(current); + } + if (balance < -1 && new->priority < current->right->priority) + { + current->right = rightRotate(current->right); + return leftRotate(current); + } + return current; +} + +void insertProcHigh(high_queue *queue, struct proc *proc) +{ + avl_tree *tree = queue->tree; + avl_node *node = (avl_node *)malloc(sizeof(avl_node)); + node->proc = proc; + node->left = 0; + node->right = 0; + node->height = 1; + if (tree->root == 0) + { + tree->root = node; + } + else + { + tree->root = insertRec(tree->root, node); + } + tree->size++; +} diff --git a/high_queue.h b/high_queue.h new file mode 100644 index 0000000000..53631071fa --- /dev/null +++ b/high_queue.h @@ -0,0 +1,29 @@ +#ifndef HGH_QUEUE_H +#define HGH_QUEUE_H + +typedef struct avl_node +{ + struct proc *proc; + struct avl_node *left; + struct avl_node *right; + int priority; + int height; +} avl_node; +typedef struct avl_tree +{ + int size; + struct avl_node *root; +} avl_tree; +/* + * CFS (Completely Fair Scheduler) + */ +typedef struct high_queue +{ + struct proc *proc; + struct avl_tree *tree; + +} high_queue; + +struct proc *nextProcHigh(high_queue *queue); +void insertProcHigh(high_queue *queue, struct proc *proc); +#endif \ No newline at end of file diff --git a/images/default/retime_metrics.png b/images/default/retime_metrics.png new file mode 100644 index 0000000000..46edfca07e Binary files /dev/null and b/images/default/retime_metrics.png differ diff --git a/images/default/rutime_metrics.png b/images/default/rutime_metrics.png new file mode 100644 index 0000000000..3ff5721916 Binary files /dev/null and b/images/default/rutime_metrics.png differ diff --git a/images/default/stime_iobound_metrics.png b/images/default/stime_iobound_metrics.png new file mode 100644 index 0000000000..59618023ce Binary files /dev/null and b/images/default/stime_iobound_metrics.png differ diff --git a/images/default/stime_metrics.png b/images/default/stime_metrics.png new file mode 100644 index 0000000000..eedc210b14 Binary files /dev/null and b/images/default/stime_metrics.png differ diff --git a/images/low_interv/retime_metrics.png b/images/low_interv/retime_metrics.png new file mode 100644 index 0000000000..536f921d0b Binary files /dev/null and b/images/low_interv/retime_metrics.png differ diff --git a/images/low_interv/rutime_metrics.png b/images/low_interv/rutime_metrics.png new file mode 100644 index 0000000000..dd4867741f Binary files /dev/null and b/images/low_interv/rutime_metrics.png differ diff --git a/images/low_interv/stime_iobound_metrics.png b/images/low_interv/stime_iobound_metrics.png new file mode 100644 index 0000000000..1e4cb6bd39 Binary files /dev/null and b/images/low_interv/stime_iobound_metrics.png differ diff --git a/images/low_interv/stime_metrics.png b/images/low_interv/stime_metrics.png new file mode 100644 index 0000000000..6da35761ce Binary files /dev/null and b/images/low_interv/stime_metrics.png differ diff --git a/list.c b/list.c new file mode 100644 index 0000000000..788901d9f7 --- /dev/null +++ b/list.c @@ -0,0 +1,73 @@ +#include "list.h" +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "x86.h" +#include "proc.h" +#include "spinlock.h" +struct listproc * createListProc() +{ + struct listproc *list = (struct listproc *)kalloc(); + list->head = 0; + list->tail = 0; + list->size = 0; + return list; +} +void destroyListProc(struct listproc *list) +{ + struct procnode *aux = list->head; + struct procnode *auxNext; + for (int i = 0; i < list->size; i++) + { + auxNext = aux->next; + kfree((char *)aux); + aux = auxNext; + } + kfree((char *)list); +} +void insertProc(struct listproc *list, struct proc *proc) +{ + procnode * node = (procnode *)kalloc(); + node->proc = proc; + node->next = 0; + if (list->size == 0) + { + list->head = node; + list->tail = node; + + } + else + { + list->tail->next = node; + list->tail = node; + } + list->size++; +} + +void removeProc(struct listproc *list, struct proc *proc) +{ + struct procnode *aux = list->head; + + if (aux->proc == proc) + { + list->head = aux->next; + list->size--; + return; + } + + struct procnode *auxPrev = list->head; + for (int i = 1; i < list->size; i++) + { + aux = aux->next; + if (aux->proc == proc) + { + auxPrev->next = aux->next; + // free(aux); + list->size--; + return; + } + auxPrev = aux; + } +} \ No newline at end of file diff --git a/list.h b/list.h new file mode 100644 index 0000000000..c53ece4988 --- /dev/null +++ b/list.h @@ -0,0 +1,17 @@ +typedef struct listproc +{ + struct procnode *head; + struct procnode *tail; + int size; +} listproc; + +typedef struct procnode +{ + struct proc *proc; + struct procnode *next; +} procnode; + +struct listproc* createListProc(); +void destroyListProc(struct listproc *list); +void insertProc(struct listproc *list, struct proc *proc); +void removeProc(struct listproc *list, struct proc *proc); \ No newline at end of file diff --git a/medium_queue.c b/medium_queue.c new file mode 100644 index 0000000000..68fdbb672e --- /dev/null +++ b/medium_queue.c @@ -0,0 +1,47 @@ +#include "medium_queue.h" + +struct proc *nextProcMedium(medium_queue queue){ + if (queue.current != 0) + { + /* TODO: atualizar a prioritade do processo */ + int newPriority = (queue.current->ticks - queue.currentVRInitial)%20; + + insertProc(&queue.listExpired[newPriority], queue.current); + queue.current = 0; + } + + for (int i = queue.currentListIndex; i < 20; i++) + { + if (queue.listActive[i].size > 0) + { + procnode *aux = queue.listActive[i].head; + struct proc *proc = aux->proc; + removeProc(&queue.listActive[i], proc); + queue.current = proc; + queue.currentVRInitial = proc->ticks; + queue.currentListIndex = i; + return proc; + } + } + + queue.currentListIndex = 0; + struct listproc aux[20] = queue.listExpired; + *queue.listExpired = *queue.listActive; + *queue.listActive = *aux; + free(aux); + + for (int i = queue.currentListIndex; i < 20; i++) + { + if (queue.listActive[i].size > 0) + { + procnode *aux = queue.listActive[i].head; + struct proc *proc = aux->proc; + removeProc(&queue.listActive[i], proc); + queue.current = proc; + queue.currentVRInitial = proc->ticks; + queue.currentListIndex = i; + return proc; + } + } + return 0; +} \ No newline at end of file diff --git a/medium_queue.h b/medium_queue.h new file mode 100644 index 0000000000..949793cd73 --- /dev/null +++ b/medium_queue.h @@ -0,0 +1,20 @@ +#ifndef MEDIUM_QUEUE_H +#define MEDIUM_QUEUE_H + +#include "list.h"; +#include "proc.h" +/* + * O(1) Scheduling Normal Processes + */ +typedef struct medium_queue +{ + struct proc *current; + int currentVRInitial; + int currentListIndex; + struct listproc listActive[20]; + struct listproc listExpired[20]; +} medium_queue; + +struct proc *nextProcMedium(medium_queue queue); + +#endif \ No newline at end of file diff --git a/output.txt b/output.txt new file mode 100644 index 0000000000..4839e67266 --- /dev/null +++ b/output.txt @@ -0,0 +1,70 @@ +xv6... +cpu0: starting 0 +sb: size 1000 nblocks 941 ninodes 200 nlog 30 logstart 2 inodestart 32 bmap start 58 +init: starting sh +$ sanity 20 +pid: 4, type: CPU_BOUND, retime: 5, rutime: 1, stime: 0 +pid: 7, type: CPU_BOUND, retime: 2, rutime: 1, stime: 0 +pid: 10, type: CPU_BOUND, retime: 10, rutime: 1, stime: 0 +pid: 13, type: CPU_BOUND, retime: 7, rutime: 1, stime: 0 +pid: 16, type: CPU_BOUND, retime: 4, rutime: 1, stime: 0 +pid: 19, type: CPU_BOUND, retime: 1, rutime: 1, stime: 0 +pid: 22, type: CPU_BOUND, retime: 10, rutime: 1, stime: 0 +pid: 25, type: CPU_BOUND, retime: 7, rutime: 1, stime: 0 +pid: 28, type: CPU_BOUND, retime: 4, rutime: 1, stime: 0 +pid: 31, type: CPU_BOUND, retime: 1, rutime: 1, stime: 0 +pid: 5, type: S_BOUND, retime: 111, rutime: 21, stime: 0 +pid: 8, type: S_BOUND, retime: 121, rutime: 21, stime: 0 +pid: 11, type: S_BOUND, retime: 112, rutime: 21, stime: 0 +pid: 14, type: S_BOUND, retime: 122, rutime: 21, stime: 0 +pid: 17, type: S_BOUND, retime: 134, rutime: 21, stime: 0 +pid: 61, type: CPU_BOUND, retime: 66, rutime: 1, stime: 0 +pid: 58, type: CPU_BOUND, retime: 74, rutime: 1, stime: 0 +pid: 34, type: CPU_BOUND, retime: 103, rutime: 1, stime: 0 +pid: 37, type: CPU_BOUND, retime: 106, rutime: 1, stime: 0 +pid: 40, type: CPU_BOUND, retime: 109, rutime: 1, stime: 0 +pid: 43, type: CPU_BOUND, retime: 111, rutime: 1, stime: 0 +pid: 46, type: CPU_BOUND, retime: 114, rutime: 1, stime: 0 +pid: 49, type: CPU_BOUND, retime: 117, rutime: 1, stime: 0 +pid: 52, type: CPU_BOUND, retime: 120, rutime: 1, stime: 0 +pid: 55, type: CPU_BOUND, retime: 157, rutime: 2, stime: 0 +pid: 20, type: S_BOUND, retime: 229, rutime: 21, stime: 0 +pid: 23, type: S_BOUND, retime: 280, rutime: 21, stime: 0 +pid: 26, type: S_BOUND, retime: 295, rutime: 21, stime: 0 +pid: 29, type: S_BOUND, retime: 342, rutime: 21, stime: 0 +pid: 32, type: S_BOUND, retime: 336, rutime: 21, stime: 0 +pid: 35, type: S_BOUND, retime: 383, rutime: 21, stime: 0 +pid: 38, type: S_BOUND, retime: 399, rutime: 21, stime: 0 +pid: 41, type: S_BOUND, retime: 443, rutime: 22, stime: 0 +pid: 44, type: S_BOUND, retime: 457, rutime: 21, stime: 0 +pid: 47, type: S_BOUND, retime: 498, rutime: 21, stime: 0 +pid: 50, type: S_BOUND, retime: 512, rutime: 21, stime: 0 +pid: 53, type: S_BOUND, retime: 543, rutime: 22, stime: 0 +pid: 56, type: S_BOUND, retime: 562, rutime: 21, stime: 0 +pid: 59, type: S_BOUND, retime: 596, rutime: 21, stime: 0 +pid: 62, type: S_BOUND, retime: 607, rutime: 21, stime: 0 +pid: 6, type: IO_BOUND, retime: 215, rutime: 101, stime: 383157 +pid: 9, type: IO_BOUND, retime: 311, rutime: 101, stime: 385072 +pid: 12, type: IO_BOUND, retime: 410, rutime: 101, stime: 384975 +pid: 15, type: IO_BOUND, retime: 508, rutime: 101, stime: 384878 +pid: 18, type: IO_BOUND, retime: 606, rutime: 101, stime: 384781 +pid: 21, type: IO_BOUND, retime: 708, rutime: 101, stime: 384652 +pid: 24, type: IO_BOUND, retime: 810, rutime: 101, stime: 384551 +pid: 27, type: IO_BOUND, retime: 912, rutime: 101, stime: 384450 +pid: 30, type: IO_BOUND, retime: 1015, rutime: 101, stime: 384348 +pid: 63, type: IO_BOUND, retime: 2174, rutime: 101, stime: 383175 +pid: 33, type: IO_BOUND, retime: 1121, rutime: 101, stime: 384233 +pid: 36, type: IO_BOUND, retime: 1229, rutime: 101, stime: 384126 +pid: 39, type: IO_BOUND, retime: 1337, rutime: 101, stime: 384019 +pid: 42, type: IO_BOUND, retime: 1445, rutime: 101, stime: 383911 +pid: 45, type: IO_BOUND, retime: 1554, rutime: 101, stime: 383803 +pid: 48, type: IO_BOUND, retime: 1664, rutime: 101, stime: 383694 +pid: 51, type: IO_BOUND, retime: 1774, rutime: 101, stime: 383585 +pid: 54, type: IO_BOUND, retime: 1885, rutime: 101, stime: 383474 +pid: 57, type: IO_BOUND, retime: 1993, rutime: 101, stime: 383367 +pid: 60, type: IO_BOUND, retime: 2049, rutime: 101, stime: 383312 +Process averages +type: CPU_BOUND, retime: 18, rutime: 0, turnaround: 19 +type: S_BOUND, retime: 118, rutime: 7, turnaround: 125 +type: IO_BOUND, retime: 395, rutime: 33, turnaround: 128455 +$ \ No newline at end of file diff --git a/param.h b/param.h index a7e90efff4..d6ae82e260 100644 --- a/param.h +++ b/param.h @@ -11,4 +11,7 @@ #define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log #define NBUF (MAXOPBLOCKS*3) // size of disk block cache #define FSSIZE 1000 // size of file system in blocks - +#define INTERV 1 +#define E1TO2 200 +#define E2TO3 100 +#define E3TO4 50 \ No newline at end of file diff --git a/proc.c b/proc.c index 806b1b184b..8be4c0247a 100644 --- a/proc.c +++ b/proc.c @@ -6,8 +6,10 @@ #include "x86.h" #include "proc.h" #include "spinlock.h" +#include "list.h" -struct { +struct +{ struct spinlock lock; struct proc proc[NPROC]; } ptable; @@ -20,32 +22,32 @@ extern void trapret(void); static void wakeup1(void *chan); -void -pinit(void) +void pinit(void) { initlock(&ptable.lock, "ptable"); } // Must be called with interrupts disabled -int -cpuid() { - return mycpu()-cpus; +int cpuid() +{ + return mycpu() - cpus; } // Must be called with interrupts disabled to avoid the caller being // rescheduled between reading lapicid and running through the loop. -struct cpu* +struct cpu * mycpu(void) { int apicid, i; - - if(readeflags()&FL_IF) + + if (readeflags() & FL_IF) panic("mycpu called with interrupts enabled\n"); - + apicid = lapicid(); // APIC IDs are not guaranteed to be contiguous. Maybe we should have // a reverse map, or reserve a register to store &cpus[i]. - for (i = 0; i < ncpu; ++i) { + for (i = 0; i < ncpu; ++i) + { if (cpus[i].apicid == apicid) return &cpus[i]; } @@ -54,8 +56,9 @@ mycpu(void) // Disable interrupts so that we are not rescheduled // while reading proc from the cpu structure -struct proc* -myproc(void) { +struct proc * +myproc(void) +{ struct cpu *c; struct proc *p; pushcli(); @@ -65,12 +68,12 @@ myproc(void) { return p; } -//PAGEBREAK: 32 -// Look in the process table for an UNUSED proc. -// If found, change state to EMBRYO and initialize -// state required to run in the kernel. -// Otherwise return 0. -static struct proc* +// PAGEBREAK: 32 +// Look in the process table for an UNUSED proc. +// If found, change state to EMBRYO and initialize +// state required to run in the kernel. +// Otherwise return 0. +static struct proc * allocproc(void) { struct proc *p; @@ -78,8 +81,8 @@ allocproc(void) acquire(&ptable.lock); - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) - if(p->state == UNUSED) + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) + if (p->state == UNUSED) goto found; release(&ptable.lock); @@ -88,11 +91,16 @@ allocproc(void) found: p->state = EMBRYO; p->pid = nextpid++; - + p->times_picked = 0; + p->rutime = 0; + p->priority = LOW; + p->stime = 0; + p->ctime = ticks; + p->retime = 0; release(&ptable.lock); - // Allocate kernel stack. - if((p->kstack = kalloc()) == 0){ + if ((p->kstack = kalloc()) == 0) + { p->state = UNUSED; return 0; } @@ -100,33 +108,31 @@ allocproc(void) // Leave room for trap frame. sp -= sizeof *p->tf; - p->tf = (struct trapframe*)sp; + p->tf = (struct trapframe *)sp; // Set up new context to start executing at forkret, // which returns to trapret. sp -= 4; - *(uint*)sp = (uint)trapret; + *(uint *)sp = (uint)trapret; sp -= sizeof *p->context; - p->context = (struct context*)sp; + p->context = (struct context *)sp; memset(p->context, 0, sizeof *p->context); p->context->eip = (uint)forkret; - return p; } -//PAGEBREAK: 32 -// Set up first user process. -void -userinit(void) +// PAGEBREAK: 32 +// Set up first user process. +void userinit(void) { struct proc *p; extern char _binary_initcode_start[], _binary_initcode_size[]; p = allocproc(); - + initproc = p; - if((p->pgdir = setupkvm()) == 0) + if ((p->pgdir = setupkvm()) == 0) panic("userinit: out of memory?"); inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); p->sz = PGSIZE; @@ -137,7 +143,7 @@ userinit(void) p->tf->ss = p->tf->ds; p->tf->eflags = FL_IF; p->tf->esp = PGSIZE; - p->tf->eip = 0; // beginning of initcode.S + p->tf->eip = 0; // beginning of initcode.S safestrcpy(p->name, "initcode", sizeof(p->name)); p->cwd = namei("/"); @@ -155,18 +161,20 @@ userinit(void) // Grow current process's memory by n bytes. // Return 0 on success, -1 on failure. -int -growproc(int n) +int growproc(int n) { uint sz; struct proc *curproc = myproc(); sz = curproc->sz; - if(n > 0){ - if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0) + if (n > 0) + { + if ((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0) return -1; - } else if(n < 0){ - if((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0) + } + else if (n < 0) + { + if ((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0) return -1; } curproc->sz = sz; @@ -177,25 +185,26 @@ growproc(int n) // Create a new process copying p as the parent. // Sets up stack to return as if from system call. // Caller must set state of returned proc to RUNNABLE. -int -fork(void) +int fork(void) { int i, pid; struct proc *np; struct proc *curproc = myproc(); // Allocate process. - if((np = allocproc()) == 0){ + if ((np = allocproc()) == 0) + { return -1; } - // Copy process state from proc. - if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0){ + if ((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0) + { kfree(np->kstack); np->kstack = 0; np->state = UNUSED; return -1; } + np->priority = MEDIUM; np->sz = curproc->sz; np->parent = curproc; *np->tf = *curproc->tf; @@ -203,8 +212,8 @@ fork(void) // Clear %eax so that fork returns 0 in the child. np->tf->eax = 0; - for(i = 0; i < NOFILE; i++) - if(curproc->ofile[i]) + for (i = 0; i < NOFILE; i++) + if (curproc->ofile[i]) np->ofile[i] = filedup(curproc->ofile[i]); np->cwd = idup(curproc->cwd); @@ -224,19 +233,20 @@ fork(void) // Exit the current process. Does not return. // An exited process remains in the zombie state // until its parent calls wait() to find out it exited. -void -exit(void) +void exit(void) { struct proc *curproc = myproc(); struct proc *p; int fd; - if(curproc == initproc) + if (curproc == initproc) panic("init exiting"); // Close all open files. - for(fd = 0; fd < NOFILE; fd++){ - if(curproc->ofile[fd]){ + for (fd = 0; fd < NOFILE; fd++) + { + if (curproc->ofile[fd]) + { fileclose(curproc->ofile[fd]); curproc->ofile[fd] = 0; } @@ -253,10 +263,12 @@ exit(void) wakeup1(curproc->parent); // Pass abandoned children to init. - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->parent == curproc){ + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->parent == curproc) + { p->parent = initproc; - if(p->state == ZOMBIE) + if (p->state == ZOMBIE) wakeup1(initproc); } } @@ -269,22 +281,24 @@ exit(void) // Wait for a child process to exit and return its pid. // Return -1 if this process has no children. -int -wait(void) +int wait(void) { struct proc *p; int havekids, pid; struct proc *curproc = myproc(); - + acquire(&ptable.lock); - for(;;){ + for (;;) + { // Scan through table looking for exited children. havekids = 0; - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->parent != curproc) + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->parent != curproc) continue; havekids = 1; - if(p->state == ZOMBIE){ + if (p->state == ZOMBIE) + { // Found one. pid = p->pid; kfree(p->kstack); @@ -301,57 +315,262 @@ wait(void) } // No point waiting if we don't have any children. - if(!havekids || curproc->killed){ + if (!havekids || curproc->killed) + { release(&ptable.lock); return -1; } // Wait for children to exit. (See wakeup1 call in proc_exit.) - sleep(curproc, &ptable.lock); //DOC: wait-sleep + sleep(curproc, &ptable.lock); // DOC: wait-sleep } } -//PAGEBREAK: 42 -// Per-CPU process scheduler. -// Each CPU calls scheduler() after setting itself up. -// Scheduler never returns. It loops, doing: -// - choose a process to run -// - swtch to start running that process -// - eventually that process transfers control -// via swtch back to the scheduler. -void -scheduler(void) -{ +int wait2(int *retime, int *rutime, int *stime){ struct proc *p; + int havekids; + struct proc *curproc = myproc(); + + acquire(&ptable.lock); + for (;;) + { + // Scan through table looking for exited children. + havekids = 0; + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->parent != curproc) + continue; + havekids = 1; + if (p->state == ZOMBIE) + { + // Found one. + *retime = p->retime; + *rutime = p->rutime; + *stime = p->stime; + release(&ptable.lock); + return wait(); + } + } + + // No point waiting if we don't have any children. + if (!havekids || curproc->killed) + { + release(&ptable.lock); + return -1; + } + + // Wait for children to exit. (See wakeup1 call in proc_exit.) + sleep(curproc, &ptable.lock); // DOC: wait-sleep + } +} +// Round robin + struct proc *round_robin_realtime_priority() +{ + struct proc *next_proc = 0; + int older_cycle = -1; + for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->state != RUNNABLE || p->priority != REALTIME) + { + continue; + } + if (older_cycle == -1 || p->last_cycle < older_cycle) + { + older_cycle = p->last_cycle; + next_proc = p; + } + } + return next_proc; +} +/** + * Greatest time usage, based on CFS, giving more processor time to the process with the greatest time_running/times_chosen + * @return struct proc* the process with the greatest time_running/times_chosen + */ +struct proc *lower_rutime_high_priority() +{ + struct proc *io_bound_process = 0; + int rutime_lower = -1; + for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->state == RUNNABLE || p->priority == HIGH) + { + if (rutime_lower == -1 || rutime_lower >= p->rutime) + { + rutime_lower = p->rutime; + io_bound_process = p; + } + } + } + return io_bound_process; +} +/** + * Lowest picked by scheduler, based on CFS, giving more processor time to the process with the lowest times_picked + * @return struct proc* the process with the lowest times_picked + */ +struct proc *lowest_picked_medium_priority() +{ + struct proc *lowest_picked = 0; + int times_picked = -1; + for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->state != RUNNABLE || p->priority != MEDIUM) + { + continue; + } + if (times_picked == -1 || times_picked >= p->times_picked) + { + times_picked = p->times_picked; + lowest_picked = p; + } + } + return lowest_picked; +} +/** + * First Come First Served + * @return struct proc* the oldest process with last_cycle + */ +struct proc *fcfs_low_priority() +{ + struct proc *oldest_proc = 0; + int oldest_ctime = -1; + for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->state != RUNNABLE || p->priority != LOW) + { + continue; + } + if (oldest_ctime == -1 || p->ctime < oldest_ctime) + { + oldest_ctime = p->ctime; + oldest_proc = p; + } + } + return oldest_proc; +} +/** + * Switch to the next process in the queue that is runnable + */ +struct proc *premptProcess(void) +{ + struct proc *next_proc = 0; + next_proc = round_robin_realtime_priority(); + if (next_proc <= 0) + { + next_proc = lower_rutime_high_priority(); + } + if(next_proc == 0){ + next_proc = lowest_picked_medium_priority(); + } + if (next_proc <= 0) + { + next_proc = fcfs_low_priority(); + } + + return next_proc; +} +int change_prio(int priority) +{ + + switch (priority) + { + case 1: + return LOW; + break; + case 2: + return MEDIUM; + break; + case 3: + return HIGH; + break; + case 4: + return REALTIME; + break; + default: + panic("Invalid priority"); + return -1; + } + return 0; +} +void process_aging() +{ + for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->state == SLEEPING) + { + p->stime++; + } + if (p->state != RUNNABLE) + { + continue; + } + p->retime++; + if (p->priority == LOW && p->retime >= E1TO2) + { + p->priority = change_prio(2); + } + else if (p->priority == MEDIUM && p->retime >= E2TO3) + { + p->priority = change_prio(3); + } + else if (p->priority == HIGH && p->retime >= E3TO4) + { + p->priority = change_prio(4); + } + } +} +// PAGEBREAK: 42 +// Per-CPU process scheduler. +// Each CPU calls scheduler() after setting itself up. +// Scheduler never returns. It loops, doing: +// - choose a process to run +// - swtch to start running that process +// - eventually that process transfers control +// via swtch back to the scheduler. +void scheduler(void) +{ + struct cpu *c = mycpu(); + struct proc *current = 0; c->proc = 0; - - for(;;){ + int prempt = 0; + for (prempt = 0; 1; prempt++) + { // Enable interrupts on this processor. sti(); - // Loop over process table looking for process to run. acquire(&ptable.lock); - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->state != RUNNABLE) + process_aging(); + if (prempt == INTERV) + { + prempt = 0; + struct proc *next = premptProcess(); + if (next <= 0) + { + current = 0; + c->proc = 0; + release(&ptable.lock); continue; - - // Switch to chosen process. It is the process's job - // to release ptable.lock and then reacquire it - // before jumping back to us. - c->proc = p; - switchuvm(p); - p->state = RUNNING; - - swtch(&(c->scheduler), p->context); + } + current = next; + current->times_picked++; + current->last_cycle = ticks; + } + if (current != 0 && (current->state == RUNNABLE || current->state == RUNNING)) + { + c->proc = current; + current->rutime++; + switchuvm(current); + current->state = RUNNING; + swtch(&(c->scheduler), current->context); switchkvm(); - - // Process is done running for now. - // It should have changed its p->state before coming back. c->proc = 0; } - release(&ptable.lock); + else + { + c->proc = 0; + } + release(&ptable.lock); } } @@ -362,19 +581,18 @@ scheduler(void) // be proc->intena and proc->ncli, but that would // break in the few places where a lock is held but // there's no process. -void -sched(void) +void sched(void) { int intena; struct proc *p = myproc(); - if(!holding(&ptable.lock)) + if (!holding(&ptable.lock)) panic("sched ptable.lock"); - if(mycpu()->ncli != 1) + if (mycpu()->ncli != 1) panic("sched locks"); - if(p->state == RUNNING) + if (p->state == RUNNING) panic("sched running"); - if(readeflags()&FL_IF) + if (readeflags() & FL_IF) panic("sched interruptible"); intena = mycpu()->intena; swtch(&p->context, mycpu()->scheduler); @@ -382,10 +600,9 @@ sched(void) } // Give up the CPU for one scheduling round. -void -yield(void) +void yield(void) { - acquire(&ptable.lock); //DOC: yieldlock + acquire(&ptable.lock); // DOC: yieldlock myproc()->state = RUNNABLE; sched(); release(&ptable.lock); @@ -393,14 +610,14 @@ yield(void) // A fork child's very first scheduling by scheduler() // will swtch here. "Return" to user space. -void -forkret(void) +void forkret(void) { static int first = 1; // Still holding ptable.lock from scheduler. release(&ptable.lock); - if (first) { + if (first) + { // Some initialization functions must be run in the context // of a regular process (e.g., they call sleep), and thus cannot // be run from main(). @@ -414,15 +631,14 @@ forkret(void) // Atomically release lock and sleep on chan. // Reacquires lock when awakened. -void -sleep(void *chan, struct spinlock *lk) +void sleep(void *chan, struct spinlock *lk) { struct proc *p = myproc(); - - if(p == 0) + + if (p == 0) panic("sleep"); - if(lk == 0) + if (lk == 0) panic("sleep without lk"); // Must acquire ptable.lock in order to @@ -431,8 +647,9 @@ sleep(void *chan, struct spinlock *lk) // guaranteed that we won't miss any wakeup // (wakeup runs with ptable.lock locked), // so it's okay to release lk. - if(lk != &ptable.lock){ //DOC: sleeplock0 - acquire(&ptable.lock); //DOC: sleeplock1 + if (lk != &ptable.lock) + { // DOC: sleeplock0 + acquire(&ptable.lock); // DOC: sleeplock1 release(lk); } // Go to sleep. @@ -445,28 +662,28 @@ sleep(void *chan, struct spinlock *lk) p->chan = 0; // Reacquire original lock. - if(lk != &ptable.lock){ //DOC: sleeplock2 + if (lk != &ptable.lock) + { // DOC: sleeplock2 release(&ptable.lock); acquire(lk); } } -//PAGEBREAK! -// Wake up all processes sleeping on chan. -// The ptable lock must be held. +// PAGEBREAK! +// Wake up all processes sleeping on chan. +// The ptable lock must be held. static void wakeup1(void *chan) { struct proc *p; - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) - if(p->state == SLEEPING && p->chan == chan) + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) + if (p->state == SLEEPING && p->chan == chan) p->state = RUNNABLE; } // Wake up all processes sleeping on chan. -void -wakeup(void *chan) +void wakeup(void *chan) { acquire(&ptable.lock); wakeup1(chan); @@ -476,17 +693,18 @@ wakeup(void *chan) // Kill the process with the given pid. // Process won't exit until it returns // to user space (see trap in trap.c). -int -kill(int pid) +int kill(int pid) { struct proc *p; acquire(&ptable.lock); - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->pid == pid){ + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->pid == pid) + { p->killed = 1; // Wake process from sleep if necessary. - if(p->state == SLEEPING) + if (p->state == SLEEPING) p->state = RUNNABLE; release(&ptable.lock); return 0; @@ -496,37 +714,37 @@ kill(int pid) return -1; } -//PAGEBREAK: 36 -// Print a process listing to console. For debugging. -// Runs when user types ^P on console. -// No lock to avoid wedging a stuck machine further. -void -procdump(void) +// PAGEBREAK: 36 +// Print a process listing to console. For debugging. +// Runs when user types ^P on console. +// No lock to avoid wedging a stuck machine further. +void procdump(void) { static char *states[] = { - [UNUSED] "unused", - [EMBRYO] "embryo", - [SLEEPING] "sleep ", - [RUNNABLE] "runble", - [RUNNING] "run ", - [ZOMBIE] "zombie" - }; + [UNUSED] "unused", + [EMBRYO] "embryo", + [SLEEPING] "sleep ", + [RUNNABLE] "runble", + [RUNNING] "run ", + [ZOMBIE] "zombie"}; int i; struct proc *p; char *state; uint pc[10]; - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->state == UNUSED) + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) + { + if (p->state == UNUSED) continue; - if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) + if (p->state >= 0 && p->state < NELEM(states) && states[p->state]) state = states[p->state]; else state = "???"; cprintf("%d %s %s", p->pid, state, p->name); - if(p->state == SLEEPING){ - getcallerpcs((uint*)p->context->ebp+2, pc); - for(i=0; i<10 && pc[i] != 0; i++) + if (p->state == SLEEPING) + { + getcallerpcs((uint *)p->context->ebp + 2, pc); + for (i = 0; i < 10 && pc[i] != 0; i++) cprintf(" %p", pc[i]); } cprintf("\n"); diff --git a/proc.h b/proc.h index 1647114179..58bdaa3abb 100644 --- a/proc.h +++ b/proc.h @@ -1,30 +1,32 @@ // Per-CPU state -struct cpu { - uchar apicid; // Local APIC ID - struct context *scheduler; // swtch() here to enter scheduler - struct taskstate ts; // Used by x86 to find stack for interrupt - struct segdesc gdt[NSEGS]; // x86 global descriptor table - volatile uint started; // Has the CPU started? - int ncli; // Depth of pushcli nesting. - int intena; // Were interrupts enabled before pushcli? - struct proc *proc; // The process running on this cpu or null +struct cpu +{ + uchar apicid; // Local APIC ID + struct context *scheduler; // swtch() here to enter scheduler + struct taskstate ts; // Used by x86 to find stack for interrupt + struct segdesc gdt[NSEGS]; // x86 global descriptor table + volatile uint started; // Has the CPU started? + int ncli; // Depth of pushcli nesting. + int intena; // Were interrupts enabled before pushcli? + struct proc *proc; // The process running on this cpu or null }; extern struct cpu cpus[NCPU]; extern int ncpu; -//PAGEBREAK: 17 -// Saved registers for kernel context switches. -// Don't need to save all the segment registers (%cs, etc), -// because they are constant across kernel contexts. -// Don't need to save %eax, %ecx, %edx, because the -// x86 convention is that the caller has saved them. -// Contexts are stored at the bottom of the stack they -// describe; the stack pointer is the address of the context. -// The layout of the context matches the layout of the stack in swtch.S -// at the "Switch stacks" comment. Switch doesn't save eip explicitly, -// but it is on the stack and allocproc() manipulates it. -struct context { +// PAGEBREAK: 17 +// Saved registers for kernel context switches. +// Don't need to save all the segment registers (%cs, etc), +// because they are constant across kernel contexts. +// Don't need to save %eax, %ecx, %edx, because the +// x86 convention is that the caller has saved them. +// Contexts are stored at the bottom of the stack they +// describe; the stack pointer is the address of the context. +// The layout of the context matches the layout of the stack in swtch.S +// at the "Switch stacks" comment. Switch doesn't save eip explicitly, +// but it is on the stack and allocproc() manipulates it. +struct context +{ uint edi; uint esi; uint ebx; @@ -32,23 +34,47 @@ struct context { uint eip; }; -enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; +enum procstate +{ + UNUSED, + EMBRYO, + SLEEPING, + RUNNABLE, + RUNNING, + ZOMBIE +}; + +enum procpriority +{ + LOW, + MEDIUM, + HIGH, + REALTIME +}; // Per-process state -struct proc { - uint sz; // Size of process memory (bytes) - pde_t* pgdir; // Page table - char *kstack; // Bottom of kernel stack for this process - enum procstate state; // Process state - int pid; // Process ID - struct proc *parent; // Parent process - struct trapframe *tf; // Trap frame for current syscall - struct context *context; // swtch() here to run process - void *chan; // If non-zero, sleeping on chan - int killed; // If non-zero, have been killed - struct file *ofile[NOFILE]; // Open files - struct inode *cwd; // Current directory - char name[16]; // Process name (debugging) +struct proc +{ + uint sz; // Size of process memory (bytes) + pde_t *pgdir; // Page table + char *kstack; // Bottom of kernel stack for this process + enum procstate state; // Process state + enum procpriority priority; // Process priority + int pid; // Process ID + struct proc *parent; // Parent process + struct trapframe *tf; // Trap frame for current syscall + struct context *context; // swtch() here to run process + void *chan; // If non-zero, sleeping on chan + int killed; // If non-zero, have been killed + struct file *ofile[NOFILE]; // Open files + struct inode *cwd; // Current directory + char name[16]; // Process name (debugging) + uint ctime; // Tick that process has been created; + int stime; // Number of ticks the process has been in the SLEEPING state + int rutime; // Number of ticks the process has run for + int last_cycle; // Last Tick that process has been choosen to run + int times_picked; // Number of times the process has been chosen to run + int retime; // Tempo READY(RUNNABLE) time }; // Process memory is laid out contiguously, low addresses first: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..4844502930 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +matplotlib==3.4.2 diff --git a/result b/result new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sanity.c b/sanity.c new file mode 100644 index 0000000000..1b1bd81029 --- /dev/null +++ b/sanity.c @@ -0,0 +1,143 @@ +#include "param.h" +#include "types.h" +#include "stat.h" +#include "user.h" +#include "fs.h" +#include "fcntl.h" +#include "syscall.h" +#include "traps.h" +#include "memlayout.h" +void empty_loop(int n) +{ + for (int i = 0; i < n; i++) + { + ; + } +} +enum proctype +{ + CPU_BOUND, + S_BOUND, + IO_BOUND +}; + +char *proctype_string[] = { + "CPU_BOUND", + "S_BOUND", + "IO_BOUND"}; + +void io_bound_loop() +{ + for (int j = 0; j < 100; j++) + { + sleep(1); + } + exit(); +} +void s_bound_loop() +{ + for (int j = 0; j < 20; j++) + { + empty_loop(1000000); + yield(); + } + exit(); +} +void cpu_bound_loop() +{ + for (int j = 0; j < 100; j++) + { + empty_loop(1000000); + } + exit(); +} + +void run_bound_loop(enum proctype type) +{ + switch (type) + { + case CPU_BOUND: + cpu_bound_loop(); + case S_BOUND: + s_bound_loop(); + case IO_BOUND: + io_bound_loop(); + default: + break; + } +} +typedef struct process_info +{ + int pid; + enum proctype type; +} process_info; + +int get_process_index(int pid, process_info *process_pids, int numberProcesses) +{ + for (int i = 0; i < numberProcesses; i++) + { + if (process_pids[i].pid == pid) + { + return i; + } + } + return -1; +} +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + printf(1, "Invalid number of arguments passed\n"); + exit(); + } + + int amount = atoi(argv[1]); + int numberProcesses = amount*3; + + int retime, rutime, stime; + int total[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + + process_info *process_pids = malloc(numberProcesses * sizeof(process_info)); + process_info process_info; + int processCreated = 0; + for (int i = 0; i < numberProcesses; i++) + { + int pid = fork(); + if (pid < 0) + { + printf(1, "fork failed\n"); + break; + } + processCreated++; + enum proctype type = i % 3; + if (pid) + { + process_info.pid = pid; + process_info.type = type; + process_pids[i] = process_info; + continue; + } + run_bound_loop(type); + } + for (int i = 0; i < processCreated; i++) + { + int wait_pid = wait2(&retime, &rutime, &stime); + int process_index = get_process_index(wait_pid, process_pids, numberProcesses); + process_info = process_pids[process_index]; + enum proctype type = process_info.type; + printf(1, "pid: %d, type: %s, retime: %d, rutime: %d, stime: %d\n", process_info.pid, proctype_string[type], retime, rutime, stime); + total[type][0] += retime; + total[type][1] += rutime; + total[type][2] += stime + rutime + retime; + } + printf(1, "Process averages\n"); + for (int i = 0; i < 3; i++) + { + printf(1, "type: %s, retime: %d, rutime: %d, turnaround: %d\n", proctype_string[i], + total[i][0] / processCreated, + total[i][1] / processCreated, + total[i][2] / processCreated); + } + exit(); + return 0; +} diff --git a/sanity_ouput_analyser.py b/sanity_ouput_analyser.py new file mode 100644 index 0000000000..7a86eaed54 --- /dev/null +++ b/sanity_ouput_analyser.py @@ -0,0 +1,62 @@ +import re +import matplotlib.pyplot as plt + +def parse_data(data): + process_data = {} + lines = data.split('\n') + sanity_seen = False + turnaround_seen = False + + for line in lines: + if "sanity" in line: + sanity_seen = True + if "turnaround" in line: + turnaround_seen = True + # Pegar o começo do sanity antes das médias + if sanity_seen and not turnaround_seen: + if "pid" in line: + match = re.search(r'pid: (\d+), type: (\w+), retime: (\d+), rutime: (\d+), stime: (\d+)', line) + if match: + pid = int(match.group(1)) + ptype = match.group(2) + retime = int(match.group(3)) + rutime = int(match.group(4)) + stime = int(match.group(5)) + if ptype not in process_data: + process_data[ptype] = {'retime': [], 'rutime': [], 'stime': []} + process_data[ptype]['retime'].append(retime) + process_data[ptype]['rutime'].append(rutime) + process_data[ptype]['stime'].append(stime) + + return process_data + +def save_plot(type): + plt.xlabel('Process - Ordered by Conclusion Time') + plt.ylabel(type) + plt.title(type+' Metrics') + plt.legend() + plt.savefig(f'images/{type}_metrics.png') + + +def plot_type(process_data, type): + plt.figure() + for ptype, metrics in process_data.items(): + plt.plot(metrics[type], label=ptype) + save_plot(type) + +def plot_iobound_stime(process_data): + plt.figure() + for ptype, metrics in process_data.items(): + if ptype == 'IO_BOUND': + plt.plot(metrics['stime'], label=ptype) + + save_plot('stime_iobound') +with open('output.txt', 'r') as file: + data = file.read() + +process_data = parse_data(data) + +plot_type(process_data, 'retime') +plot_type(process_data, 'rutime') +plot_type(process_data, 'stime') +plot_iobound_stime(process_data) \ No newline at end of file diff --git a/stime_metrics.png b/stime_metrics.png new file mode 100644 index 0000000000..7d65027ffc Binary files /dev/null and b/stime_metrics.png differ diff --git a/swtch.S b/swtch.S index 63a7dcc903..7d837b74aa 100644 --- a/swtch.S +++ b/swtch.S @@ -7,7 +7,7 @@ # Switch stacks to new and pop previously-saved registers. .globl swtch -swtch: +swtch: movl 4(%esp), %eax movl 8(%esp), %edx diff --git a/syscall.c b/syscall.c index ee85261602..204bfbbc09 100644 --- a/syscall.c +++ b/syscall.c @@ -101,31 +101,35 @@ extern int sys_sbrk(void); extern int sys_sleep(void); extern int sys_unlink(void); extern int sys_wait(void); +extern int sys_wait2(void); extern int sys_write(void); extern int sys_uptime(void); +extern int sys_yield(void); static int (*syscalls[])(void) = { -[SYS_fork] sys_fork, -[SYS_exit] sys_exit, -[SYS_wait] sys_wait, -[SYS_pipe] sys_pipe, -[SYS_read] sys_read, -[SYS_kill] sys_kill, -[SYS_exec] sys_exec, -[SYS_fstat] sys_fstat, -[SYS_chdir] sys_chdir, -[SYS_dup] sys_dup, -[SYS_getpid] sys_getpid, -[SYS_sbrk] sys_sbrk, -[SYS_sleep] sys_sleep, -[SYS_uptime] sys_uptime, -[SYS_open] sys_open, -[SYS_write] sys_write, -[SYS_mknod] sys_mknod, -[SYS_unlink] sys_unlink, -[SYS_link] sys_link, -[SYS_mkdir] sys_mkdir, -[SYS_close] sys_close, + [SYS_fork] sys_fork, + [SYS_exit] sys_exit, + [SYS_wait] sys_wait, + [SYS_pipe] sys_pipe, + [SYS_read] sys_read, + [SYS_kill] sys_kill, + [SYS_exec] sys_exec, + [SYS_fstat] sys_fstat, + [SYS_chdir] sys_chdir, + [SYS_dup] sys_dup, + [SYS_getpid] sys_getpid, + [SYS_sbrk] sys_sbrk, + [SYS_sleep] sys_sleep, + [SYS_uptime] sys_uptime, + [SYS_open] sys_open, + [SYS_write] sys_write, + [SYS_mknod] sys_mknod, + [SYS_unlink] sys_unlink, + [SYS_link] sys_link, + [SYS_mkdir] sys_mkdir, + [SYS_close] sys_close, + [SYS_wait2] sys_wait2, + [SYS_yield] sys_yield, }; void diff --git a/syscall.h b/syscall.h index bc5f35651c..bb1ffee50f 100644 --- a/syscall.h +++ b/syscall.h @@ -20,3 +20,5 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_wait2 22 +#define SYS_yield 23 diff --git a/sysproc.c b/sysproc.c index 0686d295b6..f2efe446dc 100644 --- a/sysproc.c +++ b/sysproc.c @@ -26,6 +26,18 @@ sys_wait(void) return wait(); } +int +sys_wait2(void){ + int *retime, *rutime, *stime; + + if(argptr(0, (void*)&retime, sizeof(*retime)) < 0) + return -1; + if(argptr(1, (void*)&rutime, sizeof(*rutime)) < 0) + return -1; + if(argptr(2, (void*)&stime, sizeof(*stime)) < 0) + return -1; + return wait2(retime, rutime, stime); +} int sys_kill(void) { @@ -89,3 +101,8 @@ sys_uptime(void) release(&tickslock); return xticks; } + +int sys_yield(void){ + yield(); + return 0; +} \ No newline at end of file diff --git a/user.h b/user.h index 4f99c52ba6..c1c3c3f227 100644 --- a/user.h +++ b/user.h @@ -5,35 +5,36 @@ struct rtcdate; int fork(void); int exit(void) __attribute__((noreturn)); int wait(void); -int pipe(int*); -int write(int, const void*, int); -int read(int, void*, int); +int wait2(int *, int *, int *); +int pipe(int *); +int write(int, const void *, int); +int read(int, void *, int); int close(int); int kill(int); -int exec(char*, char**); -int open(const char*, int); -int mknod(const char*, short, short); -int unlink(const char*); -int fstat(int fd, struct stat*); -int link(const char*, const char*); -int mkdir(const char*); -int chdir(const char*); +int exec(char *, char **); +int open(const char *, int); +int mknod(const char *, short, short); +int unlink(const char *); +int fstat(int fd, struct stat *); +int link(const char *, const char *); +int mkdir(const char *); +int chdir(const char *); int dup(int); int getpid(void); -char* sbrk(int); +char *sbrk(int); int sleep(int); int uptime(void); - +int yield(void); // ulib.c -int stat(const char*, struct stat*); -char* strcpy(char*, const char*); -void *memmove(void*, const void*, int); -char* strchr(const char*, char c); -int strcmp(const char*, const char*); -void printf(int, const char*, ...); -char* gets(char*, int max); -uint strlen(const char*); -void* memset(void*, int, uint); -void* malloc(uint); -void free(void*); -int atoi(const char*); +int stat(const char *, struct stat *); +char *strcpy(char *, const char *); +void *memmove(void *, const void *, int); +char *strchr(const char *, char c); +int strcmp(const char *, const char *); +void printf(int, const char *, ...); +char *gets(char *, int max); +uint strlen(const char *); +void *memset(void *, int, uint); +void *malloc(uint); +void free(void *); +int atoi(const char *); diff --git a/usys.S b/usys.S index 8bfd8a1bc4..874f1d764a 100644 --- a/usys.S +++ b/usys.S @@ -29,3 +29,5 @@ SYSCALL(getpid) SYSCALL(sbrk) SYSCALL(sleep) SYSCALL(uptime) +SYSCALL(wait2) +SYSCALL(yield) \ No newline at end of file