Este projeto faz parte do PPOS v2.
O objetivo deste projeto é construir as funções básicas de gestão de tarefas usando as funções de troca de contexto vistas no projeto anterior.
Cada tarefa existente no sistema deve ter uma estrutura que a representa, ou seja, um descritor de tarefa (TCB - Task Control Block). Essa estrutura deve ser definida no arquivo kernel/tcb.h:
struct task_t { int id; // identificador da tarefa char *name; // nome da tarefa ctx_t context; // contexto da tarefa int status; // pronta, executando, terminada, ... ... // demais informações, a completar };
As seguintes funções estão definidas em kernel/task.h e devem ser implementadas em kernel/task.c:
void task_init();
Esta função é chamada por ppos.c:ppos_init e inicia as variáveis necessárias à gestão de tarefas. Neste momento, sua principal responsabilidade é definir uma tarefa para o fluxo de execução do núcleo, que pode ser chamada de task_kernel, com nome “kernel” e ID 0. Essa tarefa pode ser vista como o equivalente da função main no programa contexts.c do projeto anterior.
task_create, pois não usa uma pilha separada nem uma função separada.
struct task_t *task_create(char *name, void (*entry)(void *), void *arg);
Parâmetros:
name: nome da tarefa (útil nas mensagens na tela), pode ser nuloentry: função que será executada pela tarefaarg: parâmetro recebido pela função entry ao iniciar a tarefaEsta função cria uma nova tarefa. Para isso ela deve:
Ao ser criada, cada tarefa recebe um ID inteiro crescente, iniciando com 0 (zero).
int task_destroy(struct task_t *task);
Esta função destrói um descritor de tarefa e libera seus recursos (pilha e TCB).
int task_switch(struct task_t *task);
Parâmetros:
task: tarefa que irá receber o processador, ou NULL.
Esta é a operação básica de troca de contexto entre tarefas, que usa a função ctx_swap da biblioteca de troca de contextos. Ela será chamada sempre que for necessária uma troca de contexto.
Caso task seja nulo, a CPU deve ser transferida para a tarefa que criou a tarefa atual, ou seja, a tarefa que estava ativa quando task_create criou a tarefa que está executando neste momento.
task_switch recebe somente o ponteiro da próxima tarefa, então ela deve ter alguma forma de saber quem é a tarefa atualmente em execução, para poder efetuar a troca de contexto.
int task_id(struct task_t *task);
Esta função informa o identificador numérico (ID) de uma tarefa (ou da tarefa tarefa atual, se task for nulo). Não devem existir duas tarefas com o mesmo ID.
char *task_name(struct task_t *task);
Esta função informa o nome de uma tarefa (ou da tarefa atual, se task for nulo).
Será necessário implementar um despachante de tarefas básico na função dispatcher.c:dispatcher. Ele deve executar estas ações:
task_user para executar o código inicial da aplicação (definido na função pingpong-task*.c:user_main)task_switch.
A função dispatcher é acionada pelo programa principal do SO (função ppos.c:main); quando essa função encerrar, o programa principal irá desligar o SO.
A implementação completa deste projeto compreende definir as estruturas de dados necessários para gerenciar as tarefas e implementar as funções acima descritas, comentando detalhadamente o código.
Avisos de compilação (warnings) geram desconto na nota!
Seu código deve funcionar adequadamente com os programas de teste, fornecendo as saídas esperadas:
test/pingpong-tasks1.c (saída esperada test/pingpong-tasks1.txt)test/pingpong-tasks2.c (saída esperada test/pingpong-tasks2.txt)test/pingpong-tasks3.c (saída esperada test/pingpong-tasks3.txt)
A compilação usando make irá gerar os executáveis dos programas de teste acima.
contexts.c do projeto anterior, reescrito com as funções propostas neste projeto. Comparar os dois códigos pode ajudar a compreender o que deve ser implementado em cada função.
Os seguintes arquivos são relevantes para este projeto:
kernel/tcb.h: define a estrutura do task control blockkernel/task.h: interface da gestão básica de tarefas (não alterar)kernel/task.c: implementação da gestão básica de tarefaskernel/dispatcher.c: implementação básica do despachante de tarefastest/pingpong-tasks*.c: programas de testetest/pingpong-tasks*.txt: saídas esperadas dos programas de testeTodas as funções implementadas devem gerar mensagens de depuração, que permitam acompanhar a execução das tarefas, como no exemplo a seguir:
PPOS: system starting DEBUG: subsystem task initiated DEBUG: subsystem dispatcher initiated PPOS: system started (uptime 0 ms) DEBUG: dispatcher started DEBUG: task 0 (kernel) create task 1 (user) DEBUG: task 0 (kernel) switch to task 1 (user) user: inicio DEBUG: task 1 (user) create task 2 (ping) DEBUG: task 1 (user) create task 3 (pong) DEBUG: task 1 (user) switch to task 2 (ping) ping: inicio ping: 0 DEBUG: task 2 (ping) switch to task 3 (pong) pong: inicio pong: 0 DEBUG: task 3 (pong) switch to task 2 (ping) ping: 1 DEBUG: task 2 (ping) switch to task 3 (pong) pong: 1 DEBUG: task 3 (pong) switch to task 2 (ping) ping: 2 DEBUG: task 2 (ping) switch to task 3 (pong) pong: 2 DEBUG: task 3 (pong) switch to task 2 (ping) ping: 3 DEBUG: task 2 (ping) switch to task 3 (pong) pong: 3 DEBUG: task 3 (pong) switch to task 2 (ping) ping: fim DEBUG: task 2 (ping) switch to task 1 (user) DEBUG: task 1 (user) switch to task 3 (pong) pong: fim DEBUG: task 3 (pong) switch to task 1 (user) user: fim DEBUG: task 1 (user) destroy task 2 (ping) DEBUG: task 1 (user) destroy task 3 (pong) DEBUG: task 1 (user) switch to task 0 (kernel) DEBUG: dispatcher stopping, no more user tasks DEBUG: task 0 (kernel) destroy task 1 (user) PPOS: system stopping PPOS: system stopped (uptime 0 ms)
Para facilitar seu trabalho, o arquivo kernel/macros.h define uma macro ppos_debug para a geração de mensagens de depuração, que funciona igual à função printf:
#ifdef DEBUG ppos_debug("switch task %d (%s) to task %d (%s)\n", ...); #endif
Essas mensagens de depuração somente serão geradas se a macro global DEBUG estiver definida no código-fonte (#define DEBUG) ou na linha de comando, no momento da compilação (make debug p1).