Meu Kernel - Minha Vida

Este post faz parte da série Meu Kernel, Minha Vida. Leia também os outros posts da série:

Olá caro leitor, tudo bem? Neste artigo gostaria de compartilhar o resultado dos meus estudos, relacionado ao desenvolvimento de kernel. A inspiração para o estudo surgiu após ver o conteúdo da palestra do professor Rodrigo Almeida fez para a ESC Embedded Systems Conference 2017 Silicon Valley e pelo Felipe Neves, que vem desenvolvendo o seu próprio sistema operacional de tempo real (RTOS), o uLipeRTOS.

Para os meus estudos utilizei o material que o professor Rodrigo Almeida disponibilizada aqui no Embarcados e na sua página pessoal. Outra fonte que consultei foi o livro The Definitive Guide to the ARM Cortex-M0 do autor Joseph Yiu, além de estudar o código fonte de diferentes RTOS's.

 

Para este artigo vou apresentar um kernel multi-tarefas cooperativo, bem simples, com níveis de prioridade na execução das tarefas. As principais características de um sistema com núcleo cooperativo são:

  • o código fonte pode ser todo escrito em linguagem C, não necessitando de nenhuma característica especial do hardware;
  • necessita que todas tarefas/processos terminem, para possibilitar que outra tarefa possa ser executada;
  • rotina com loop infinito podem travar o sistema (para o kernel aqui apresentado, loop infinito vai travar  a aplicação em um determinado processo).

 

Para mais informações sobre kernel e sistemas operacionais recomendo a leitura dos seguintes artigos:

 

Justificativa

 

Para quem já está familiarizado com o desenvolvimento de firmware com RTOS, sabe como é cômodo estruturar a sua aplicação, dividindo em diversas tarefas, deixando a cargo do RTOS o escalonamento delas.

 

Como é de conhecimento de muitos desenvolvedores, existem uma quantidade enorme de aplicações que não necessitam das características de um sistema de tempo real. E os RTOS's são aplicados mesmo assim, só pelas facilidades em estruturar o firmware.

 

O ponto negativo dessa abordagem é que os RTOS's sempre ocupam uma boa fatia de memória RAM e memória ROM, e a maiorias dos microcontroladores possui uma quantidade bem reduzida desses recursos. Com a proposta do kernel aqui apresentado, é possível ter as mesmas características do escalonador existente em um RTOS, assim facilitando a estruturação do firmware.

 

O kernel

 

O kernel desenvolvido conta com escalonador de tarefas e diversas APIs (funções) para manipular os processos. Então, basicamente o kernel se resume em apenas no escalonador de tarefas.

 

Projeto

 

O projeto de demonstração utiliza a Freedom Board KE06Z, que possui um microcontrolador com núcleo ARM Cortex-M0. E uma característica interessante dessa arquitetura é o temporizador "SysTick". Trata-se de uma interrupção periódica, própria do núcleo. É através dela que o kernel obtém a sua base de tempo.

 

Nota: Para arquiteturas diferentes, é necessário reservar um "Timer", para fornecer a base de tempo para o kernel.

 

Basicamente o projeto conta com vetor, de estrutura previamente definida, onde são inseridas as tarefas e suas configurações. E num segundo momento o escalonador assume o controle e passa a gerenciar este vetor para decidir qual tarefa deve ser executada.

 

A seguir é apresentado o código fonte do kernel desenvolvido, para a melhor compreensão do projeto (kernel.c e kernel.h).

 

kernel.c

 

kernel.h

 

Tarefas

 

As tarefas no kernel possuem 4 estados, que são:

  • pronta (Task_Ready);
  • em pausa (Task_Paused);
  • bloqueada (Task_Blocked);
  • deletada (Task_Deleted).

 

As tarefas também são agrupadas em prioridades. O núcleo do sistema disponibiliza 3 níveis de prioridades: baixa (Priority_Low), média (Priority_Medium) e alta (Priority_High). Além das prioridades citadas, o kernel possui a prioridade dedicada à tarefa ociosa (Task_Idle com prioridade Priority_Idle). Trata-se da tarefa que é executada quando não houver nenhuma tarefa no estado pronta. A mesma pode e deve ser utilizada pela aplicação, sendo que o kernel não executa nenhum processo nesta tarefa.

 

O kernel, entre aspa, acaba criando listas de tarefas para cada nível de prioridade. E assim, o escalonador de tarefas seleciona qual tarefa deve ser executada pela CPU. Outra característica do núcleo é, o escalonamento entras as tarefas de mesma prioridade, que trabalham no modo FIFO (First In, First Out - quer dizer que a primeira tarefa da fila será a primeira a ser executada). A imagem a seguir demonstra com maiores detalhes o agrupamento de tarefas feito pelo o escalonador.

 

Organização das tarefas feito escalonador
Figura 1 - Organização das tarefas feito escalonador

 

API's do Sistema

Principais Funções:

Como dito o kernel possui algumas API's para manipular os processos. Como não poderia faltar, tem a API “kernel_init”, é a primeira função do sistema que deve ser executada. É nesta função que serão inicializadas as variáveis de controle do sistema.

 

A segunda função a ser executada é a “kernel_add_task”, através da qual são adicionadas as tarefas da aplicação ao kernel. E, além de inserir as tarefas ao sistema, é por ela que são configurados os parâmetros das tarefas (prioridade e estado inicial).

E por fim temos a API “kernel_run”, onde se encontra o escalonador das tarefas do sistema.

 

Funções Adicionais: 

 

Como dito, o kernel aqui apresentado é cooperativo, e é necessário que cada processo termine para que outra tarefa seja executada. O mesmo provê algumas API's para manipular os parâmetros das tarefas. Assim possibilitando que as tarefas possam ser; pausadas, bloqueadas, desbloqueadas e deletadas.

 

A primeira API é “kernel_task_delay”, através da qual é possível pausar a execução da tarefa por um determinado tempo. Ao final desse período, o kernel se encarrega de alterar o seu estado, para o status pronta (Task_Ready).

Outra API para manipular o estado da tarefa é a função “kernel_task_delete”, que faz com que a tarefa não seja mais executada pelo kernel.

Nota: Quando é executada essa API, o kernel não exclui a tarefa do vetor. Portanto, não libera espaço em memória.

 

A funções “kernel_task_blocks” e “kernel_task_unlock” servem para bloquear e desbloquear a execução da tarefa pelo sistema.

 

O kernel também possui API para a troca de prioridade das tarefas, a função “kernel_task_set_priority”. 

 

E como foi dito anteriormente, o kernel possui a tarefa ociosa (task_Idle), one o usuário poder estar adicionando um processo. Isso é feito através da função “kernel_add_task_idle”.

 

Projeto de Demonstração

 

O Projeto que escolhi para demonstração, é bem simples. O algoritmo consiste em verificar os Push Buttons (SW2, SW3) e acionar o LED RGB que estão presente na Freedom Board KE06Z.

 

A seguir é apresentado o código fonte da aplicação (main.c, app.c e app.h).

 

main.c

 

app.c

 

app.h

 

Resultado

 

O resultado da aplicação desenvolvida é: temos o LED verde piscando em alta frequência, pois o sistema gasta pouco tempo, para verificar os Push Buttons e a CPU do microcontrolador passa grande parte do tempo executando a tarefa ociosa (IdleTask). Então temos a sensação que o LED verde está o tempo todo acionado.

Quando pressionado o Push Button SW2, o LED vermelho começa a piscar. O mesmo vale para o Push Button SW3, que faz com que o LED azul pisque. A seguir temos figura 2, demonstrando o resultado da aplicação.

 

Resultado da Aplicação
Figura 2 - Resultado da Aplicação

 

Conclusão

 

O kernel aqui apresentado é fruto dos meus estudos sobre o assunto, não tem como objetivo se tornar uma alternativa para substituir sistemas operacionais já existentes no mercado. Mas sim, demonstrar ao caro leitor, que é possível desenvolver soluções simples para estruturar firmware, não se limitando apenas ao uso de um RTOS ou do Super Loop.

 

Próximos passos... É estudar sobre a troca de contexto e as técnicas existentes para o mesmo. E assim conseguir desenvolver kernel preemptivo. Assim que for evoluindo nos meus estudos, prometo trazer mais artigos relacionados ao assunto.

 

O código fonte do projeto com aplicação e kernel deixei disponível no meu Github. E fica aqui o meu convite a você caro leitor, que se interessou pelo assunto, a contribuir com o projeto, testando e aperfeiçoando o mesmo.

 

Referências 

 

The Definitive Guide to the ARM Cortex-M0
Desenvolvendo um RTOS: Introdução
Desenvolvendo um RTOS: processos e tarefas
Desenvolvendo um RTOS: Utilizando buffer circular como gestor de processos

Outros artigos da série

Meu Kernel Minha Vida - Round-Robin >>
Este post faz da série Meu Kernel, Minha Vida. Leia também os outros posts da série:
NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Evandro Teixeira
Desenvolvedor de Sistemas Embarcados. Sou formado Técnico em Instrumentação e Automação Industrial/Mecatrônica pelo Colégio Salesiano Dom Bosco de Americana-SP, cursei o Engenharia Elétrica com Ênfase em Eletrônica pela UNISAL Centro Universitário Salesiano de São Paulo e atualmente estou cursando Superior de Tecnologia em Análise e Desenvolvimento de Sistemas pela UNIP Universidade Paulista.

Deixe um comentário

avatar
 
  Notificações  
Notificar