Mudanças entre as edições de "BOOOS - Basic Object Oriented Operating System"
m (Desfeita a edição 112287 de Arliones.hoeller (Discussão)) |
|||
(49 revisões intermediárias por 2 usuários não estão sendo mostradas) | |||
Linha 1: | Linha 1: | ||
Neste página encontram-se os enunciados de atividades do projeto de ensino BOOOS - Basic Object Oriented Operating System. O projeto é constituído de N atividades, descritas abaixo. | Neste página encontram-se os enunciados de atividades do projeto de ensino BOOOS - Basic Object Oriented Operating System. O projeto é constituído de N atividades, descritas abaixo. | ||
− | |||
− | {| | + | {{collapse top| bg=lightyellow | expandir=true | Sobre mecanismo de testes}} |
− | | | + | == Sobre mecanismo de testes == |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | | | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Os testes que passei a vocês são um programa de testes automatizados. Vocês não precisam modificá-los. Ao executar o teste, vocẽs verão uma saída como esta: | |
− | |||
− | |||
− | |||
− | Os testes que passei a vocês são | ||
<syntaxhighlight lang=bash> | <syntaxhighlight lang=bash> | ||
− | arliones.hoeller@sj-labdes-29463:~ | + | arliones.hoeller@sj-labdes-29463:~/my_booos/test$ ./Scheduler_Test |
Welcome to BOOOS - Basic Object Oriented Operating System! | Welcome to BOOOS - Basic Object Oriented Operating System! | ||
This program will test the class: Scheduler | This program will test the class: Scheduler | ||
Linha 65: | Linha 20: | ||
Para não realizar o teste automático, basta editar a função main do teste e chamar diretamente a função de teste que deseja executar (exemplo: ''Priority_Scheduler_Test_Functions::test_scheduling_without_aging()''). | Para não realizar o teste automático, basta editar a função main do teste e chamar diretamente a função de teste que deseja executar (exemplo: ''Priority_Scheduler_Test_Functions::test_scheduling_without_aging()''). | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Um projeto pré-configurado para o Elicpse também foi disponibilizado [http://docente.ifsc.edu.br/arliones.hoeller/sop/booos-t1.tgz aqui]. Para utilizar este projeto, baixe o Eclipse com CDT para C/C++. Se preferir, utilize o ambiente pré-configurado disponibilizado [http://wiki.sj.ifsc.edu.br/index.php/Arliones_Hoeller#Material_de_apoio aqui]. | |
− | + | Observe que a pasta do projeto possui Makefiles. Se preferir, não é necessário utilizar o Eclipse para compilar e testar o projeto, basta usar os seguintes comandos, a partir do diretório do projeto (booos-t1): | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | Observe que a pasta do projeto possui Makefiles. Se preferir, não é necessário utilizar o Eclipse para compilar e testar o projeto, basta usar os seguintes comandos, a partir do diretório do projeto (booos- | ||
<syntaxhighlight lang=bash> | <syntaxhighlight lang=bash> | ||
$ make all #Compila sistema e gera aplicação padrão | $ make all #Compila sistema e gera aplicação padrão | ||
$ ./booos #Executa aplicação padrão | $ ./booos #Executa aplicação padrão | ||
− | $ make TEST= | + | $ make TEST=Task_Test test #Compila programa de teste |
− | $ ./test/ | + | $ ./test/Task_Test #Executa programa de teste |
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | {{collapse bottom}} | |
− | + | ||
− | + | {{collapse top| bg=lightyellow | expandir=true | t0: troca de contexto e tarefas cooperativas}} | |
− | |||
− | |||
− | |||
− | |||
− | = | + | = t0: troca de contexto e tarefas cooperativas = |
Neste trabalho deve-se estender o projeto sendo desenvolvido no curso com a construção de uma classe para abstrair processos em nível de usuários - na prática, threads. A classe implementada será chamada de '''Task''' (tarefa). Lembre-se que uma classe é uma estrutura de dados, logo, nossa classe '''Task''' será o PCB ('''Proccess Control Block''') do sistema. A partir deste trabalho, será disponibilizado um gabarito em C++ como no código abaixo, geralmente incompleto, e um diagrama UML de uma versão completa da solução implementada pelo professor, como na imagem abaixo. | Neste trabalho deve-se estender o projeto sendo desenvolvido no curso com a construção de uma classe para abstrair processos em nível de usuários - na prática, threads. A classe implementada será chamada de '''Task''' (tarefa). Lembre-se que uma classe é uma estrutura de dados, logo, nossa classe '''Task''' será o PCB ('''Proccess Control Block''') do sistema. A partir deste trabalho, será disponibilizado um gabarito em C++ como no código abaixo, geralmente incompleto, e um diagrama UML de uma versão completa da solução implementada pelo professor, como na imagem abaixo. | ||
Linha 188: | Linha 57: | ||
namespace BOOOS { | namespace BOOOS { | ||
− | class Task | + | class Task { |
public: | public: | ||
enum State { | enum State { | ||
Linha 226: | Linha 95: | ||
O teste no arquivo test/Task_Test.cc implementa uma aplicação para testar sua classe Task. Use este exemplo como teste inicial. Abaixo há uma descrição detalhada dos métodos de interface da classe. | O teste no arquivo test/Task_Test.cc implementa uma aplicação para testar sua classe Task. Use este exemplo como teste inicial. Abaixo há uma descrição detalhada dos métodos de interface da classe. | ||
+ | |||
+ | :'''static void init()''': método de classe que precisa ser chamando na inicialização do sistema e deve inicializar os atributos de classe (static). '''Atenção, o atributo __main, que é static, deve ser inicializado aqui!''' | ||
:'''Task(void (*entry_point)(void *), int nargs, void * arg)''': construtor - deve inicializar todos os atributos dos '''objetos'''. | :'''Task(void (*entry_point)(void *), int nargs, void * arg)''': construtor - deve inicializar todos os atributos dos '''objetos'''. | ||
Linha 241: | Linha 112: | ||
:'''static Task * self()''': método de classe (static) que retorna a Task executando no momento. | :'''static Task * self()''': método de classe (static) que retorna a Task executando no momento. | ||
− | |||
+ | [http://docente.ifsc.edu.br/arliones.hoeller/sop/booos-t0.tgz Aqui] há um projeto do Eclipse pré-configurado com os gabaritos para o t0. | ||
− | + | {{collapse bottom}} | |
+ | {{collapse top| bg=lightyellow | expandir=true | t1: Escalonador FCFS e por prioridades}} | ||
− | + | = t1: Escalonador FCFS e por prioridades = | |
− | = | ||
Você irá construir um despachante de tarefas baseado em duas entidades: uma tarefa ''dispatcher'', responsável pelo controle geral, e uma função ''choose_next'', responsável por determinar qual a próxima tarefa a executar a cada troca de contexto. A figura abaixo ilustra o funcionamento geral do sistema (fonte Prof. Maziero/UTFPR): | Você irá construir um despachante de tarefas baseado em duas entidades: uma tarefa ''dispatcher'', responsável pelo controle geral, e uma função ''choose_next'', responsável por determinar qual a próxima tarefa a executar a cada troca de contexto. A figura abaixo ilustra o funcionamento geral do sistema (fonte Prof. Maziero/UTFPR): | ||
Linha 271: | Linha 142: | ||
#include <Task.h> | #include <Task.h> | ||
− | |||
namespace BOOOS { | namespace BOOOS { | ||
Linha 329: | Linha 199: | ||
*''choose_next()'': este método '''virtual''' é o responsável por implementar a política de escalonamento empregada. Neste trabalho utilizaremos uma política FCFS (''First-Come First-Served''). '''Dica:''' pense na relação entre esta política e a fila que implementamos - esta função deve ser extremamente simples! | *''choose_next()'': este método '''virtual''' é o responsável por implementar a política de escalonamento empregada. Neste trabalho utilizaremos uma política FCFS (''First-Come First-Served''). '''Dica:''' pense na relação entre esta política e a fila que implementamos - esta função deve ser extremamente simples! | ||
− | + | A aplicação de teste está disponível [http://docente.ifsc.edu.br/arliones.hoeller/sop/Scheduler_Test.cc aqui]. Algumas outras observações: | |
− | * Como nosso sistema está ficando mais complexo, o lib/BOOOS.h e lib/BOOOS.cc estão sendo utilizados para configurar o sistema. Ele tem uma função ''init'' que chamará os ''inits'' dos outros componentes. Ele também tem parâmetros de configuração do escalonamento (política, preempção e envelhecimento de prioridades) | + | * Como nosso sistema está ficando mais complexo, o lib/BOOOS.h e lib/BOOOS.cc estão sendo utilizados para configurar o sistema. Ele tem uma função ''init'' que chamará os ''inits'' dos outros componentes. Ele também tem parâmetros de configuração do escalonamento (política, preempção e envelhecimento de prioridades). |
* O arquivo Scheduler.h disponibilizado está completo, ou seja, você não precisa modificá-lo, a não ser que queira. | * O arquivo Scheduler.h disponibilizado está completo, ou seja, você não precisa modificá-lo, a não ser que queira. | ||
* Serão necessárias algumas mudanças na classe Task: | * Serão necessárias algumas mudanças na classe Task: | ||
Linha 337: | Linha 207: | ||
** A fila de tarefas prontas (_ready) deve ser um atributo de classe (static) de ''Task''. Tasks devem ser incluídas na fila quando criadas e removidas quando destruídas. | ** A fila de tarefas prontas (_ready) deve ser um atributo de classe (static) de ''Task''. Tasks devem ser incluídas na fila quando criadas e removidas quando destruídas. | ||
** A classe Scheduler é ''friend'' da classe Task. Isto significa que Scheduler pode manipular os atributos protegidos de Task. | ** A classe Scheduler é ''friend'' da classe Task. Isto significa que Scheduler pode manipular os atributos protegidos de Task. | ||
− | |||
== Algoritmos de escalonamento == | == Algoritmos de escalonamento == | ||
Linha 347: | Linha 216: | ||
[[Arquivo:Booos-prio.png|600px]] | [[Arquivo:Booos-prio.png|600px]] | ||
− | |||
− | |||
# ''Task::nice(int p)'': nossa Task terá um método chamado ''nice'' que configura a prioridade da tarefa ajustando o valor de seu ''_rank''. O escalonador ''DEVE'' utilizar prioridades no estilo UNIX, ou seja, com valores entre -20 e 20. ''Curiosidade:'' abra um terminal e digite '''man nice'''; | # ''Task::nice(int p)'': nossa Task terá um método chamado ''nice'' que configura a prioridade da tarefa ajustando o valor de seu ''_rank''. O escalonador ''DEVE'' utilizar prioridades no estilo UNIX, ou seja, com valores entre -20 e 20. ''Curiosidade:'' abra um terminal e digite '''man nice'''; | ||
− | # ''SCHEDULER_TYPE'': parâmetro de configuração do sistema que define o tipo escalonador utilizado. ''Dica:'' utilize este parâmetro para decidir se o sistema de utilizar ''Queue::insert'' ou ''Queue::insert_ordered''. | + | # O arquivo ''BOOOS.h'' deve incluir na classe BOOOS as seguintes constantes de configuração: |
+ | ## ''SCHEDULER_TYPE'': parâmetro de configuração do sistema que define o tipo escalonador utilizado. ''Dica:'' utilize este parâmetro para decidir se o sistema de utilizar ''Queue::insert'' ou ''Queue::insert_ordered''. | ||
+ | ## ''SCHED_PREEMPT'': parâmetro de configuração do sistema que define se o escalonamento é preemptivo (SCHED_PREEMPT == true) ou não-preemptivo (SCHED_PREEMPT == false). ''Dica:'' verifique no material da aula quando um escalonador deve agir para trocar um processo sendo escalonado se for preemptivo ou não-preeptivo. Depois, adapte seu código para satisfazer estas diferenças. | ||
+ | ## ''SCHED_AGING'': parâmetro de configuração do sistema que define se o envelhecimento de prioridades está ligado (SCHED_AGING == true) ou desligado (SCHED_AGING == false). Um problema conhecido do escalonamento por prioridades é a inanição (''starvation'') de tarefas de baixa prioridade, que ocorre se houverem tarefas de alta prioridade prontas para executar a todo tempo. Para resolver isto, um mecanismo de envelhecimento (aging) de prioridades deve ser implementado. Este mecanismo deve executar sempre que o escalonador for acionado, e deve envelhecer a prioridade daquelas tarefas que continuam em espera. É importante lembrar que depois que uma tarefa executa, ao voltar para a fila de prontos ela deve possuir sua prioridade original (não envelhecida). | ||
+ | |||
+ | |||
+ | '''Baixe aqui os [http://docente.ifsc.edu.br/arliones.hoeller/sop/booos-t1-tests.tar.gz testes do t1].''' | ||
+ | {{collapse bottom}} | ||
+ | {{collapse top| bg=lightyellow | expandir=true | t2: Main, join, exit}} | ||
− | = | + | = t2: Main, join, exit = |
== Tarefa main== | == Tarefa main== | ||
Linha 378: | Linha 253: | ||
Task *pang, *peng, *ping, *pong, *pung; | Task *pang, *peng, *ping, *pong, *pung; | ||
+ | |||
+ | void busywait_25ms() { | ||
+ | unsigned int i = 0x7ffff0; | ||
+ | while(i--) { | ||
+ | __asm__ volatile ("\tnop\n"); | ||
+ | } | ||
+ | } | ||
void function(void * arg) { | void function(void * arg) { | ||
Linha 386: | Linha 268: | ||
for(i=0; i<10; i++) { | for(i=0; i<10; i++) { | ||
cout << (char*)arg << " " << i << endl; | cout << (char*)arg << " " << i << endl; | ||
− | + | busywait_25ms(); | |
} | } | ||
cout << (char*)arg << " End" << endl; | cout << (char*)arg << " End" << endl; | ||
Linha 394: | Linha 276: | ||
int main() { | int main() { | ||
− | + | BOOOS::SCHED_POLICY = Scheduler::SCHED_PRIORITY; | |
− | + | BOOOS::SCHED_PREEMPT = true; | |
− | + | BOOOS::SCHED_AGING = true; | |
− | BOOOS:: | + | BOOOS::BOOOS booos; |
cout << "Main Start" << endl; | cout << "Main Start" << endl; | ||
Linha 419: | Linha 301: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
== Operador Join == | == Operador Join == | ||
Linha 450: | Linha 330: | ||
Task *pang, *peng, *ping, *pong, *pung; | Task *pang, *peng, *ping, *pong, *pung; | ||
+ | void busywait_25ms() { | ||
+ | unsigned int i = 0x7ffff0; | ||
+ | while(i--) { | ||
+ | __asm__ volatile ("\tnop\n"); | ||
+ | } | ||
+ | } | ||
+ | |||
void function(void * arg) { | void function(void * arg) { | ||
int i; | int i; | ||
Linha 457: | Linha 344: | ||
for(i=0; i<10; i++) { | for(i=0; i<10; i++) { | ||
cout << (char*)arg << " " << i << endl; | cout << (char*)arg << " " << i << endl; | ||
− | + | busywait_25ms(); | |
} | } | ||
cout << (char*)arg << " End" << endl; | cout << (char*)arg << " End" << endl; | ||
Linha 465: | Linha 352: | ||
int main() { | int main() { | ||
− | + | BOOOS::SCHED_POLICY = Scheduler::SCHED_PRIORITY; | |
− | + | BOOOS::SCHED_PREEMPT = true; | |
− | + | BOOOS::SCHED_AGING = true; | |
− | BOOOS:: | + | BOOOS::BOOOS booos; |
cout << "Main Start" << endl; | cout << "Main Start" << endl; | ||
Linha 513: | Linha 400: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | <!-- | ||
+ | |||
+ | {{collapse top| bg=lightyellow | expandir=true | t3: Preempção, compartilhamento de tempo e contabilização de tarefas}} | ||
− | = | + | = t3: Preempção, compartilhamento de tempo e contabilização de tarefas = |
Até este ponto nosso sistema apenas escalona tarefas de modo cooperativo, ou seja, para que a CPU passe de uma tarefa para outra é necessário que a tarefa em execução chame explicitamente ''pass_to'' ou ''yield''. Neste trabalho, adicionaremos suporte a preempção ao BOOOS. | Até este ponto nosso sistema apenas escalona tarefas de modo cooperativo, ou seja, para que a CPU passe de uma tarefa para outra é necessário que a tarefa em execução chame explicitamente ''pass_to'' ou ''yield''. Neste trabalho, adicionaremos suporte a preempção ao BOOOS. | ||
Linha 618: | Linha 510: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | == Contabilização de Tarefas == | ||
+ | |||
+ | Você irá adicionar mecanismos para contabilizar o uso do processador pelas tarefas em execução. Seu sistema deve produzir uma mensagem de saída com o seguinte formato, para cada tarefa que finaliza (incluindo o próprio dispatcher): | ||
+ | |||
+ | :Task 17 exit: response time 4955 ms, CPU time 925 ms, 171 activations | ||
+ | |||
+ | O tempo de resposta é o tempo decorrido desde que a tarefa foi criada (pelo seu construtor), até o momento em que encerra (na chamada a '''exit()'''). A Task pode utilizar a função '''Timer::time()''' para saber o instante de tempo em que o sistema se encontra em cada tempo. O tempo de CPU é o tempo em que a tarefa permanece utilizando a CPU. O número de ativações é o número de vezes que o escalonador colocou a tarefa para executar. | ||
+ | |||
== Implementação do trabalho == | == Implementação do trabalho == | ||
Linha 627: | Linha 528: | ||
*Quando um quantum é completado, o controle da tarefa deve retornar ao dispatcher. | *Quando um quantum é completado, o controle da tarefa deve retornar ao dispatcher. | ||
− | Abaixo o diagrama UML atualizado do sistema e uma versão do arquivo Timer.h COMPLETO. | + | Abaixo o diagrama UML atualizado do sistema e uma versão do arquivo Timer.h COMPLETO. |
Alguns detalhes sobre esta versão do projeto: | Alguns detalhes sobre esta versão do projeto: | ||
Linha 696: | Linha 597: | ||
#endif /* TIMER_H_ */ | #endif /* TIMER_H_ */ | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | |||
== Observações == | == Observações == | ||
Linha 705: | Linha 608: | ||
*O tratador de temporização no escalonador deve sempre verificar se a tarefa corrente é de usuário ou de sistema antes de preemptá-la devido ao fim de um quantum. Você pode usar o tipo SCHEDULER para testar isto. | *O tratador de temporização no escalonador deve sempre verificar se a tarefa corrente é de usuário ou de sistema antes de preemptá-la devido ao fim de um quantum. Você pode usar o tipo SCHEDULER para testar isto. | ||
− | + | {{collapse bottom}} | |
− | |||
− | |||
− | |||
− | |||
− | + | {{collapse top| bg=lightyellow | expandir=true | t4: Semáforo, Produtor-Consumidor e Fila de Mensagens}} | |
− | = | + | = t4: Semáforo, Produtor-Consumidor e Fila de Mensagens = |
== Construção de Semáforos == | == Construção de Semáforos == | ||
Linha 801: | Linha 700: | ||
{ | { | ||
public: | public: | ||
− | static const int MSG_SIZE = | + | static const int MSG_SIZE = BOOOS::BOOOS::MESSAGE_SIZE; |
Message(char msg[MSG_SIZE]) { memcpy(_msg, msg, MSG_SIZE); } | Message(char msg[MSG_SIZE]) { memcpy(_msg, msg, MSG_SIZE); } | ||
Linha 832: | Linha 731: | ||
Os métodos da classe devem implementar as seguintes funções: | Os métodos da classe devem implementar as seguintes funções: | ||
− | *'''Message_Queue( | + | *'''Message_Queue(int max_size)''': cria uma fila de mensagens. Cada fila pode ter, no máximo, max_size mensagens. |
*'''~Message_Queue()''': destroi uma fila de mensagens. Mensagens ainda na fila, mas não consumidas, devem ser destruídas. Tarefas bloqueadas aguardando por mensagens devem ser liberadas. | *'''~Message_Queue()''': destroi uma fila de mensagens. Mensagens ainda na fila, mas não consumidas, devem ser destruídas. Tarefas bloqueadas aguardando por mensagens devem ser liberadas. | ||
*'''send(Message & msg)''': insere uma mensagem no final da fila. A mensagem deve ser criada externamente pela aplicação. Se a fila estiver cheia, este método bloqueia a tarefa até que espaço seja liberado na fila. | *'''send(Message & msg)''': insere uma mensagem no final da fila. A mensagem deve ser criada externamente pela aplicação. Se a fila estiver cheia, este método bloqueia a tarefa até que espaço seja liberado na fila. | ||
− | *'''Message receive()''': retorna | + | *'''Message receive()''': retorna uma mensagem da fila. Se a fila estiver vazia, esta chamada bloqueia a tarefa até que uma mensagem chegue. |
*'''count()''': retorna a quantidade de mensagens na fila. | *'''count()''': retorna a quantidade de mensagens na fila. | ||
Linha 873: | Linha 772: | ||
Message_Queue::Message qmsg(msg); | Message_Queue::Message qmsg(msg); | ||
queue.send(qmsg); | queue.send(qmsg); | ||
− | + | Timer::delay(25000); | |
} | } | ||
Linha 897: | Linha 796: | ||
cout << "This program will test the class: Semaphore" << endl; | cout << "This program will test the class: Semaphore" << endl; | ||
− | + | BOOOS::BOOOS::SCHED_POLICY = BOOOS::BOOOS::SCHED_ROUNDROBIN; | |
− | + | BOOOS::BOOOS::SCHED_PREEMPT = true; | |
− | + | BOOOS::BOOOS::SCHED_AGING = true; | |
− | BOOOS:: | + | BOOOS::BOOOS booos(false); |
Task * myself = Task::self(); | Task * myself = Task::self(); | ||
− | Task prod(&producer); | + | Task prod(&producer,0,0); |
− | Task cons(&consumer); | + | Task cons(&consumer,0,0); |
− | + | prod.join(); | |
− | + | cons.join(); | |
myself->exit(0); | myself->exit(0); | ||
Linha 915: | Linha 814: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | {{collapse bottom}} | ||
+ | --> |
Edição atual tal como às 14h18min de 18 de julho de 2016
Neste página encontram-se os enunciados de atividades do projeto de ensino BOOOS - Basic Object Oriented Operating System. O projeto é constituído de N atividades, descritas abaixo.
Sobre mecanismo de testes |
---|
Sobre mecanismo de testesOs testes que passei a vocês são um programa de testes automatizados. Vocês não precisam modificá-los. Ao executar o teste, vocẽs verão uma saída como esta: arliones.hoeller@sj-labdes-29463:~/my_booos/test$ ./Scheduler_Test
Welcome to BOOOS - Basic Object Oriented Operating System!
This program will test the class: Scheduler
Starting tests for unit: Scheduler
Init: OK!
Creation and Destruction: OK!
FCFS: OK!
Priority with Aging: OK!
Priority without Aging: OK!
Para não realizar o teste automático, basta editar a função main do teste e chamar diretamente a função de teste que deseja executar (exemplo: Priority_Scheduler_Test_Functions::test_scheduling_without_aging()). Um projeto pré-configurado para o Elicpse também foi disponibilizado aqui. Para utilizar este projeto, baixe o Eclipse com CDT para C/C++. Se preferir, utilize o ambiente pré-configurado disponibilizado aqui. Observe que a pasta do projeto possui Makefiles. Se preferir, não é necessário utilizar o Eclipse para compilar e testar o projeto, basta usar os seguintes comandos, a partir do diretório do projeto (booos-t1): $ make all #Compila sistema e gera aplicação padrão
$ ./booos #Executa aplicação padrão
$ make TEST=Task_Test test #Compila programa de teste
$ ./test/Task_Test #Executa programa de teste
|
t0: troca de contexto e tarefas cooperativas |
---|
t0: troca de contexto e tarefas cooperativasNeste trabalho deve-se estender o projeto sendo desenvolvido no curso com a construção de uma classe para abstrair processos em nível de usuários - na prática, threads. A classe implementada será chamada de Task (tarefa). Lembre-se que uma classe é uma estrutura de dados, logo, nossa classe Task será o PCB (Proccess Control Block) do sistema. A partir deste trabalho, será disponibilizado um gabarito em C++ como no código abaixo, geralmente incompleto, e um diagrama UML de uma versão completa da solução implementada pelo professor, como na imagem abaixo. /*
* Task.h
*
* Created on: Aug 15, 2014
* Author: arliones
*/
#ifndef TASK_H_
#define TASK_H_
#include <Queue.h>
#include <ucontext.h>
namespace BOOOS {
class Task {
public:
enum State {
READY,
WAITING,
RUNNING,
FINISHING
};
Task(void (*entry_point)(void *), int nargs, void * arg);
virtual ~Task();
int tid() { return _tid; }
State state() { return _state; }
void pass_to(Task * t, State s = READY);
void exit(int code);
static Task * self() { return (Task*)__running; }
static void init();
private:
static volatile Task * __running;
State _state;
int _tid; // task ID
// ...
};
} /* namespace BOOOS */
#endif /* TASK_H_ */
Os métodos de interface (i.e., os públicos) que precisam ser implementados na classe estão na declaração acima. Não altere a assinatura destes métodos! Observe que você certamente precisará de novos atributos para o correto funcionamento da classe, e seria bom utilizar alguns métodos privados para auxiliar na implementação. Você pode criar métodos e atributos privados à vontade. Onde será que será declarado o ucontext_t de cada Task? O teste no arquivo test/Task_Test.cc implementa uma aplicação para testar sua classe Task. Use este exemplo como teste inicial. Abaixo há uma descrição detalhada dos métodos de interface da classe.
|
t1: Escalonador FCFS e por prioridades |
---|
t1: Escalonador FCFS e por prioridadesVocê irá construir um despachante de tarefas baseado em duas entidades: uma tarefa dispatcher, responsável pelo controle geral, e uma função choose_next, responsável por determinar qual a próxima tarefa a executar a cada troca de contexto. A figura abaixo ilustra o funcionamento geral do sistema (fonte Prof. Maziero/UTFPR): Para isto, uma nova classe será adicionada ao nosso sistema: Scheduler. Como nosso escalonador é também um processo, ele herda de Task. Uma função estática chamada dispatcher implementa o comportamento de escalonamento. Abaixo, o esqueleto C++ da classe Scheduler: /*
* Scheduler.h
*
* Created on: Mar 21, 2014
* Author: arliones
*/
#ifndef SCHEDULER_H_
#define SCHEDULER_H_
#include <Task.h>
namespace BOOOS {
class Scheduler : public Task {
friend class Task;
protected:
Scheduler();
public:
enum SchedulerType {
SCHED_FCFS,
SCHED_PRIORITY
};
virtual ~Scheduler();
static void init();
static void dispatcher(void*);
static Scheduler * self() { return __dispatcher; }
protected:
virtual Task * choose_next();
static Scheduler * __dispatcher;
};
} /* namespace BOOOS */
#endif /* SCHEDULER_H_ */
Neste trabalho, os seguintes métodos precisam ser implementados:
void dispatcher()
{
while(userTasks > 0)
{
next = choose_next() ; // escolher a próxima Task* a executar
if(next)
{
... // ações antes de lancar a tarefa "next", se houverem
self()->pass_to(next); // transfere controle para a tarefa "next"
... // ações apos retornar da tarefa "next", se houverem
}
}
exit(0) ; // encerra a tarefa dispatcher
}
A aplicação de teste está disponível aqui. Algumas outras observações:
Algoritmos de escalonamentoA implementação do dispatcher descrito acima já deve gerar uma política de escalonamento implícita. Que política é esta? Agora, a política de escalonamento inicial deve ser modificada para escalonar tarefas por prioridades. O diagrama abaixo apresenta as modificações necessárias para que o sistema escalone por prioridades.
|
t2: Main, join, exit |
---|
t2: Main, join, exitTarefa mainDesde o início temos forjado uma tarefa main na inicialização de Task. Contudo, não demos ainda toda a atenção necessária a esta tarefa. Neste trabalho, caso ainda não tenha sido feito, vocês devem extender o sistema de vocês para atender aos seguintes requisitos:
#include <iostream>
#include <queue>
#include <sstream>
#include <BOOOS.h>
#include <Scheduler.h>
#define ASSERT(x,y) if(!(x)) return y;
using namespace std;
using namespace BOOOS;
Task *pang, *peng, *ping, *pong, *pung;
void busywait_25ms() {
unsigned int i = 0x7ffff0;
while(i--) {
__asm__ volatile ("\tnop\n");
}
}
void function(void * arg) {
int i;
Task::self()->nice(2*Task::self()->tid());
for(i=0; i<10; i++) {
cout << (char*)arg << " " << i << endl;
busywait_25ms();
}
cout << (char*)arg << " End" << endl;
Task::self()->exit(0);
}
int main() {
BOOOS::SCHED_POLICY = Scheduler::SCHED_PRIORITY;
BOOOS::SCHED_PREEMPT = true;
BOOOS::SCHED_AGING = true;
BOOOS::BOOOS booos;
cout << "Main Start" << endl;
pang = new Task(function, 1, (char*)"\tPang");
peng = new Task(function, 1, (char*)"\t\tPeng");
ping = new Task(function, 1, (char*)"\t\t\tPing");
pong = new Task(function, 1, (char*)"\t\t\t\tPong");
pung = new Task(function, 1, (char*)"\t\t\t\t\tPung");
while(Task::count() > 2) {
Task::self()->nice(20);
Task::self()->yield();
}
cout << "Main End" << endl;
Task::self()->exit(0);
return 0;
}
Operador JoinO objetivo deste projeto é construir um método de sincronização denominado join que permite que uma tarefa espere a conclusão de outra tarefa, de forma similar à chamada POSIX wait para processos. Para isso, um método com a seguinte assinatura deve ser adicionado à classe Task: int join();
Ao chamar task->join(), a tarefa atual (Task::self()) deve ser suspensa até a conclusão da tarefa task. Quando a tarefa task terminar, isto é, chamar o método exit(), a tarefa suspensa deve ser colocada de volta à fila de tarefas prontas. Caso a tarefa task não exista, ou já tenha encerrado, esta chamada deve retornar imediatamente, sem suspender a tarefa atual. Lembre-se que mais de uma tarefa pode aguardar pela mesma tarefa concluir, e que todas devem ser "acordadas" quando isto acontecer. O valor de retorno do método join é o código de encerramento da tarefa task, ou seja, o valor passado como parâmetro à chamada do método exit(), ou, -1 caso a tarefa indicada não exista. Seu sistema deve funcionar com o programa exemplo a seguir. #include <iostream>
#include <queue>
#include <sstream>
#include <BOOOS.h>
#include <Scheduler.h>
#define ASSERT(x,y) if(!(x)) return y;
using namespace std;
using namespace BOOOS;
Task *pang, *peng, *ping, *pong, *pung;
void busywait_25ms() {
unsigned int i = 0x7ffff0;
while(i--) {
__asm__ volatile ("\tnop\n");
}
}
void function(void * arg) {
int i;
Task::self()->nice(2*Task::self()->tid());
for(i=0; i<10; i++) {
cout << (char*)arg << " " << i << endl;
busywait_25ms();
}
cout << (char*)arg << " End" << endl;
Task::self()->exit(2*Task::self()->tid());
}
int main() {
BOOOS::SCHED_POLICY = Scheduler::SCHED_PRIORITY;
BOOOS::SCHED_PREEMPT = true;
BOOOS::SCHED_AGING = true;
BOOOS::BOOOS booos;
cout << "Main Start" << endl;
pang = new Task(function, 1, (char*)"\tPang");
peng = new Task(function, 1, (char*)"\t\tPeng");
ping = new Task(function, 1, (char*)"\t\t\tPing");
pong = new Task(function, 1, (char*)"\t\t\t\tPong");
pung = new Task(function, 1, (char*)"\t\t\t\t\tPung");
while(Task::count() > 2) {
Task::self()->nice(20);
Task::self()->yield();
}
int result = 0;
cout << "Main wait pang... ";
result += pang->join();
cout << "Result: " << result << endl;
cout << "Main wait peng... ";
result += peng->join();
cout << "Result: " << result << endl;
cout << "Main wait ping... ";
result += ping->join();
cout << "Result: " << result << endl;
cout << "Main wait pong... ";
result += pong->join();
cout << "Result: " << result << endl;
cout << "Main wait pung... ";
result += pung->join();
cout << "Result: " << result << endl;
cout << "Main End" << endl;
Task::self()->exit(0);
return 0;
}
|