Tarefas cooperativas
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.
Descritor de tarefa
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 };
Implementação
As seguintes funções estão definidas em kernel/task.h e devem ser implementadas em kernel/task.c:
Inicia a gestão de tarefas
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.
Cria uma nova tarefa
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çãoentryao iniciar a tarefa- retorno: ponteiro para uma nova tarefa ou NULL, se houver erro
Esta função cria uma nova tarefa. Para isso ela deve:
- alocar e preencher um TCB
- alocar uma pilha
- criar um contexto
- definir um ID
- devolver um ponteiro para o TCB
Ao ser criada, cada tarefa recebe um ID inteiro crescente, iniciando com 0 (zero).
Destrói uma tarefa
int task_destroy(struct task_t *task);
Esta função destrói um descritor de tarefa e libera seus recursos (pilha e TCB).
Transfere o processador para outra tarefa
int task_switch(struct task_t *task);
Parâmetros:
task: tarefa que irá receber o processador, ou NULL.- retorno: valor negativo se houver erro, ou zero
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.
Informa o identificador da tarefa corrente
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.
Informa o nome da tarefa corrente
char *task_name(struct task_t *task);
Esta função informa o nome de uma tarefa (ou da tarefa atual, se task for nulo).
O despachante
Será necessário implementar um despachante de tarefas básico na função dispatcher.c:dispatcher. Ele deve executar estas ações:
- criar uma tarefa
task_userpara executar o código inicial da aplicação (definido na funçãopingpong-task*.c:user_main) - passar a CPU para essa tarefa, usando a função
task_switch. - quando retornar dessa tarefa, destruí-la e encerrar.
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.
Observações
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!
Validação
Seu código deve funcionar adequadamente com os programas de teste, fornecendo as saídas esperadas:
test/pingpong-tasks1.c(saída esperadatest/pingpong-tasks1.txt)test/pingpong-tasks2.c(saída esperadatest/pingpong-tasks2.txt)test/pingpong-tasks3.c(saída esperadatest/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.
Arquivos
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 teste
Depuração
Todas 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).
Outras informações
- Duração estimada: 4 horas.
- Dependências: