Toda a verdade sobre o RTOS. Artigo 14. Seções de memória: introdução e serviços básicos



Seções de memória foram mencionadas anteriormente em um dos artigos anteriores (# 6), onde foi feita uma comparação com a função padrão da linguagem C malloc () . Uma partição é uma região de memória obtida de um conjunto de partições (conjunto de memórias). O compartilhamento de memória fornece uma maneira flexível de alocar e liberar de forma confiável e determinística.

Artigos anteriores da série:
Artigo 13. Estruturas de dados da tarefa e chamadas de API não suportadas
Artigo 12. Serviços para trabalhar com tarefas
Artigo 11. Tarefas: configuração e introdução à API
Artigo 10. Agendador: recursos avançados e preservação de contexto
Artigo 9. Agendador: implementação
Artigo 8. Núcleo SE: Projeto Interno e Implantação
Artigo # 7 Núcleo SE: Introdução
Artigo 6. Outros serviços RTOS
Artigo 5. Interação e sincronização de tarefas
Artigo 4. Tarefas, alternância de contexto e interrupções
Artigo # 3 Tarefas e planejamento
Artigo 2. RTOS: estrutura e modo em tempo real
Artigo 1. RTOS: introdução.

Usando seções


No Nucleus SE, os conjuntos de partições são configurados no momento da criação. Um único aplicativo pode ter até 16 conjuntos de partições. Se eles não estiverem configurados, estruturas de dados e chamadas de serviço relacionadas a esses conjuntos não serão incluídas no aplicativo.

Um conjunto de partições é uma área de memória dividida em um determinado número de blocos de tamanho fixo. O desenvolvedor tem controle total sobre o tamanho e o número de partições em cada pool. As tarefas podem solicitar seções alocadas da memória e receber um ponteiro para a área de armazenamento e não devem gravar dados fora da seção alocada. Uma seção pode ser liberada por qualquer tarefa ao passar um ponteiro para uma função da API. Uma solicitação para alocar uma partição quando não houver partições livres pode levar a um erro ou suspensão da solicitação, dependendo dos parâmetros de chamada da API selecionados e da configuração do Nucleus SE.

Configurando partições de memória


Número de conjuntos de partições


Como na maioria dos objetos do Nucleus SE, a configuração do pool de partições é feita principalmente usando a diretiva #define em nuse_config.h . O parâmetro principal é NUSE_PARTITION_POOL_NUMBER , que determina quantos conjuntos de partições estão definidos no aplicativo. O valor padrão é 0 (ou seja, conjuntos de partições não são usados), o desenvolvedor pode definir qualquer valor de 0 a 16. Outros valores levarão a um erro de compilação, que foi detectado durante a verificação em nuse_config_check.h (incluído em nuse_config.c , e , portanto, compila com este módulo), o que leva à compilação da diretiva #error .

Escolher um valor diferente de zero é uma maneira prioritária de ativar os conjuntos de partições. Isso leva à definição de estruturas de dados e à atribuição do tamanho apropriado. As estruturas de dados na ROM devem ser inicializadas com os valores apropriados que descrevem cada conjunto de partições. Mais detalhes sobre estruturas de dados estarão no próximo artigo. Essa seleção também ativa as configurações da API.

Ativar chamadas de API


Cada função da API (chamada de utilitário) no Nucleus SE é ativada pela diretiva #define em nuse_config.h . Para conjuntos de partições, eles incluem:

NUSE_PARTITION_ALLOCATE
NUSE_PARTITION_DEALLOCATE
NUSE_PARTITION_POOL_INFORMATION
NUSE_PARTITION_POOL_COUNT

Por padrão, todos estão configurados como FALSE , desativando cada chamada de serviço e impedindo a inclusão de um código de implementação. Para configurar os conjuntos de partições no aplicativo, é necessário selecionar as chamadas de API necessárias e definir as diretivas correspondentes como TRUE .

A seguir, um trecho do arquivo nuse_config.h padrão:



Se a função API do Partition Pools estiver ativada, mas os pools não estiverem configurados, ocorrerá um erro de compilação (exceto NUSE_Partition_Pool_Count () , que sempre está ativado). Se o seu código usar uma chamada de API que não foi ativada, ocorrerá um erro de layout porque o código de implementação não foi incluído no aplicativo.

Chamadas do utilitário de conjunto de partições


O Nucleus RTOS suporta sete chamadas de utilitários relacionadas aos conjuntos de partições, que fornecem a seguinte funcionalidade:

Descrição funcionalNúcleo RTOSNúcleo SE
Seleção de seçãoNU_Allocate_Partition ()NUSE_Partition_Allocate ()
Seção LiberaçãoNU_Deallocate_Partition ()NUSE_Partition_Deallocate ()
Fornecendo informações
sobre um conjunto de partições específico
NU_Partition_Pool_Information ()NUSE_Partition_Pool_Information ()
Retornar o valor da quantidade (atualmente) configurada
pools de aplicativos
NU_Established_Partition_Pools ()NUSE_Partition_Pool_Count ()
Adicionando (criando) um novo pool de partições ao aplicativoNU_Create_Partition_Pool ()Não implementado.
Alterando (excluindo) um pool de partições de um aplicativoNU_Delete_Partition_Pool ()Não implementado.
Retornando ponteiros para todos os conjuntos de partições atualmente existentes no aplicativoNU_Partition_Pool_Pointers ()Não implementado.

A implementação de cada chamada será discutida em detalhes.

Vale ressaltar que nem o Nucleus RTOS nem o Nucleus SE possuem uma função de reinicialização. Isso é feito de propósito. Muitas vezes, uma tarefa aloca uma seção e passa um ponteiro para outra tarefa (que pode ser liberada posteriormente). Se você recarregar o conjunto de partições, todas as partições serão marcadas como não utilizadas; no entanto, não há mecanismo para monitorar e notificar todas as tarefas que podem usar partições.

Serviços de Partição e Liberação


As operações fundamentais com conjuntos de partições são a alocação de partições no conjunto (ou seja, marcando a partição como usada e retornando seu endereço) e liberando a partição (ou seja, a partição é marcada como não utilizada). O Nucleus RTOS e o Nucleus SE fornecem duas chamadas de API básicas para essas operações, descritas abaixo.

Seleção de seção


Chamar a API Nucleus RTOS para alocar uma partição é muito flexível, o que permite aos desenvolvedores pausar tarefas por um período indefinido ou sem tempo limite, se a operação não puder ser concluída imediatamente, por exemplo, quando você tenta alocar uma partição de um pool no qual todas as partições já estão distribuídas. O Nucleus SE fornece o mesmo serviço, apenas a pausa nas tarefas é opcional e o tempo limite não é implementado.

Chamada de API do núcleo RTOS para partição


Protótipo de chamada:

STATUS NU_Allocate_Partition (pool NU_PARTITION_POOL *, VOID ** return_pointer, suspensão UNSIGNED);

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_NO_PARTITION - nenhuma seção está disponível;
NU_INVALID_POOL - ponteiro inválido do conjunto de partições;
NU_INVALID_POINTER - passou um ponteiro nulo para os dados retornados ( NULL );
NU_INVALID_SUSPEND - foi feita uma tentativa de suspender uma tarefa a partir de um encadeamento não associado à tarefa;
NU_TIMEOUT - nenhuma partição está disponível, mesmo após a suspensão pelo período de espera especificado;
NU_POOL_DELETED - O pool de partições foi excluído quando a tarefa foi suspensa.

Chamada da API do Nucleus SE para destacar uma partição


Essa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.

Protótipo de chamada:

STATUS NUSE_Partition_Allocate (pool NUSE_PARTITION_POOL, ADDR * return_pointer, U8 suspende);

Parâmetros:

pool - índice (ID) do pool de partições usado;
return_pointer - ponteiro para uma variável do tipo ADDR , que leva o endereço da seção selecionada;
suspend - parâmetro para pausar a tarefa e pode assumir os valores NUSE_NO_SUSPEND ou NUSE_SUSPEND .

Valor de retorno:

NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_NO_PARTITION - nenhuma seção está disponível;
NUSE_INVALID_POOL - índice do conjunto de partições inválido;
NUSE_INVALID_POINTER - passou um ponteiro nulo para os dados retornados ( NULL );
NUSE_INVALID_SUSPEND - Foi feita uma tentativa de suspender uma tarefa a partir de um thread que não estava associado à tarefa ou quando as APIs de bloqueio foram desativadas.

Implementação de alocação de partição no Nucleus SE


O código de função da API NUSE_Partition_Allocate é selecionado usando a compilação condicional após a verificação dos parâmetros, dependendo se a chamada da API para bloquear (suspender tarefas) está ativada ou não. Abaixo, consideraremos separadamente essas duas opções.

Se as chamadas de bloqueio estão desativadas, a chamada da API é bastante simples:



Primeiro, a disponibilidade de partições livres é verificada. Se não houver essas partições, um erro será retornado ( NUSE_NO_PARTITION ). Depois, há uma enumeração de seções, durante as quais os primeiros bytes são verificados quanto a valores zero (o que indica que a seção não é usada). Quando uma partição é encontrada, é atribuído o sinalizador "usado", que inclui o índice do pool de partições (consulte "Liberar a partição" abaixo) e retorna um ponteiro para o próximo byte (o início da área de dados reais). Explicações sobre as estruturas de dados dos conjuntos de partições serão apresentadas no próximo artigo na seção Estruturas de Dados.

Se o bloqueio estiver ativado, o código para esta chamada de API se tornará um pouco mais complicado:



O código é colocado em um loop do ... while , que continua sendo executado enquanto o parâmetro de pausa for NUSE_SUSPEND .

Se nenhuma partição estiver disponível e o parâmetro pause for NUSE_NO_SUSPEND , a chamada da API será interrompida e retornará NUSE_NO_PARTITION . Se o parâmetro pause foi definido como NUSE_SUSPEND , a tarefa é pausada. Ao retornar (por exemplo, quando uma tarefa é retomada), o valor de retorno de NUSE_SUCCESS indica que a tarefa foi retomada porque a seção de memória foi liberada e o código retorna ao início do loop. Como não há funções de API para recarregar conjuntos de partições, as tarefas não podem ser retomadas por outros motivos, mas para a estabilidade de bloquear outros tipos de objetos, o processo de verificação NUSE_Task_Blocking_Return [] é salvo.

Seção Liberação


O lançamento da seção no Nucleus RTOS e Nucleus SE torna novamente disponível. Antes do lançamento, ele não verifica se esta seção é usada por alguma tarefa ou não, o programador do aplicativo é responsável por isso. Apenas um ponteiro para uma área de dados é necessário para liberar uma seção.

Chamada da API do núcleo RTOS para partição livre


Protótipo de chamada:

STATUS NU_Deallocate_Partition (partição VOID *);

Parâmetros:

partição - um ponteiro para a área de dados (retornada pela função NU_Allocate_Partition () ) da partição a ser liberada;

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_POINTER - ponteiro de seção NULL ou não indica uma seção válida usada.

Chamada da API do Nucleus SE para liberar partição


Essa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.

Protótipo de chamada:

STATUS NUSE_Partition_Deallocate (partição ADDR);

Parâmetros:

partição - um ponteiro para a área de dados (retornada pela função NUSE_Partition_Allocate () ) da partição a ser liberada

Valor de retorno:

NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_POINTER - o ponteiro da seção é nulo ( NULL ) ou não indica uma seção válida usada

Implementação


Em vez de implementar usando as funções da API de bloqueio e não bloqueio, a função NUSE_Partition_Deallocate () simplesmente contém uma seção compilada condicionalmente, responsável por desbloquear tarefas. Este código implementa a liberação de seções:



Primeiro, o índice de seção é recuperado do byte de status. Em seguida, o estado da partição muda para "não utilizado", o contador das partições usadas diminui e a função relata a conclusão bem-sucedida da operação.

Se o bloqueio estiver ativado, o código a seguir será usado para retomar as tarefas que aguardam o pool de partições disponível:



Se as tarefas foram bloqueadas ao alocar partições nesse pool, a primeira tabela é retomada.

No próximo artigo, falaremos sobre chamadas de API adicionais relacionadas a partições de memória, bem como estruturas de dados relacionadas.

Sobre o autor: Colin Walls trabalha na indústria eletrônica há mais de trinta anos, dedicando a maior parte de seu tempo ao firmware. Ele agora é engenheiro de firmware na Mentor Embedded (uma divisão da Mentor Graphics). Colin Walls frequentemente fala em conferências e seminários, autor de vários artigos técnicos e dois livros sobre firmware. Vive no Reino Unido.

Source: https://habr.com/ru/post/pt426425/


All Articles