Estou desenvolvendo principalmente dispositivos que portam o Linux, para que a biblioteca C padrão ofereça muita funcionalidade através da implementação de chamadas do sistema com comportamento padronizado.
No entanto, para o bare metal, não há SO subjacente. Existe um padrão relacionado a como a biblioteca de CA deve ser implementada ou você precisa reaprender a peculiaridade de implementações de uma biblioteca quando muda para uma nova placa que fornece um BSP diferente?
Respostas:
Sim, existe um padrão, simplesmente a biblioteca padrão C . As funções da biblioteca não requerem um sistema operacional "completo" ou qualquer sistema operacional, e existem várias implementações disponíveis sob medida para o código "bare metal", o Newlib talvez seja o mais conhecido.
Tomando o Newlib como exemplo, é necessário que você escreva um pequeno subconjunto de funções principais, principalmente como os arquivos e a alocação de memória são tratados no seu sistema. Se você estiver usando uma plataforma de destino comum, é provável que alguém já tenha feito esse trabalho por você.
Se você estiver usando linux (provavelmente também OSX e talvez até cygwin / msys?) E digite
man strlen
, ele deve ter uma seção chamada algo comoCONFORMING TO
, que informaria que a implementação está em conformidade com um padrão específico. Dessa forma, você pode descobrir se algo que você está usando é uma função padrão ou se depende de um sistema operacional específico.fonte
stdlib
implementastdio
sem depender do sistema operacional. comofopen()
,fclose()
,fread()
,fwrite()
,putc()
egetc()
? e comomalloc()
funciona sem falar com o sistema operacional?getchar
eputchar
que conhecem o UART do seu hardware; em seguida, as camadas Newlibprintf
sobre elas. A E / S de arquivo também dependerá de algumas primitivas.stdin
estdout
estderr
(que cuida deputchar()
egetchar()
) que direciona a E / S de / para um UART, se sua plataforma possui armazenamento de arquivos, como com um flash, você deve escrever cola para isso também. e você tem que ter os meios paramalloc()
efree()
. Eu acho que se você cuidar desses problemas, poderá executar C portátil em seu destino incorporado (nãoargv
nemargc
).Primeiro, o padrão C define algo chamado de implementação "independente", em oposição a uma implementação "hospedada" (que é o que a maioria de nós conhece, a gama completa de funções C suportadas pelo sistema operacional subjacente).
Uma implementação "autônoma" precisa definir apenas um subconjunto dos cabeçalhos da biblioteca C, ou seja, aqueles que não precisam de suporte ou mesmo a definição de funções (eles apenas fazem
#define
s etypedef
s):<float.h>
<iso646.h>
<limits.h>
<stdalign.h>
<stdarg.h>
<stdbool.h>
<stddef.h>
<stdint.h>
<stdnoreturn.h>
Ao dar o próximo passo em direção a uma implementação hospedada, você descobrirá que existem muito poucas funções que realmente precisam interagir com o "sistema" de qualquer forma, com o restante da biblioteca sendo implementável sobre essas "primitivas" " Ao implementar o PDCLib , fiz um esforço para isolá-los em um subdiretório separado para facilitar a identificação ao transportar a lib para uma nova plataforma (exemplos para a porta Linux entre parênteses):
getenv()
(extern char * * environ
)system()
(fork()
/execve()
/wait()
)malloc()
efree()
(brk()
/sbrk()
)_Exit()
(_exit()
)time()
(ainda não implementado)E para
<stdio.h>
(sem dúvida o mais "envolvido em SO" dos cabeçalhos C99):open()
)close()
)unlink()
)link()
/unlink()
)write()
)read()
)lseek()
)Certos detalhes da biblioteca são opcionais, com o padrão apenas oferecendo a implementação de uma maneira padrão, mas não tornando essa implementação um requisito.
A
time()
função pode legalmente retornar apenas(time_t)-1
se não houver mecânica de controle de tempo disponível.Os manipuladores de sinal descritos para
<signal.h>
não precisam ser invocados por outra coisa que não seja uma chamada pararaise()
, não há nenhum requisito de que o sistema realmente envie algo semelhanteSIGSEGV
ao aplicativo.O cabeçalho C11
<threads.h>
, que é (por razões óbvias) muito dependente do sistema operacional, não precisa ser fornecido se a implementação definir__STDC_NO_THREADS__
...Existem mais exemplos, mas não os tenho em mãos agora.
O restante da biblioteca pode ser implementado sem nenhuma ajuda do ambiente. (*)
(*) Advertência: A implementação do PDCLib ainda não está concluída, portanto, talvez eu tenha esquecido uma coisa ou duas. ;-)
fonte
O padrão C é realmente definido separadamente do ambiente operacional. Nenhuma suposição é feita sobre a presença de um SO host e as partes dependentes do host são definidas como tal.
Ou seja, o padrão C já é bastante bare metal.
Obviamente, as partes da linguagem que tanto gostamos, as bibliotecas, costumam ser o local onde a linguagem principal empurra essas coisas específicas. Portanto, o material típico do compilador cruzado "xxx-lib" encontrado para muitas ferramentas de plataforma bare metal.
fonte
Exemplo mínimo executável newlib
Aqui, forneço um exemplo altamente automatizado e documentado que mostra newlib em ação no QEMU .
Com o newlib, você implementa suas próprias chamadas de sistema para sua plataforma baremetal.
Por exemplo, no exemplo acima, temos um programa de exemplo
exit.c
:e em um arquivo C separado
common.c
, implementamos oexit
com semi - hospedagem ARM :Os outros syscalls típicos que você implementará são:
write
para produzir resultados para o host. Isso pode ser feito com:brk
paramalloc
.Fácil no baremetal, já que não precisamos nos preocupar com paginação!
TODO Gostaria de saber se é realista alcançar a execução de syscalls de agendamento preventivo sem entrar em um RTOS completo, como Zephyr ou FreeRTOS .
O interessante do Newlib é que ele implementa todas as coisas específicas que não são do SO, como
string.h
para você, e permite implementar apenas os stubs do SO.Além disso, você não precisa implementar todos os stubs, mas apenas os necessários. Por exemplo, se o seu programa precisar apenas
exit
, você não precisará fornecer aprint
.A árvore de origem Newlib já possui algumas implementações, incluindo uma implementação de semi-hospedagem do ARM
newlib/libc/sys/arm
, mas, na maioria das vezes, você precisa implementar suas próprias. No entanto, fornece uma base sólida para a tarefa.A maneira mais fácil de configurar o Newlib é criar seu próprio compilador com o crosstool-NG, basta dizer que você deseja usar o Newlib como a biblioteca C. Minha instalação lida com isso automaticamente com esse script , que usa as configurações newlib presentes em
crosstool_ng_config
.Eu acho que C ++ também funcionará, mas TODO testá-lo.
fonte
Quando você o usa baremetal, descobre algumas dependências não implementadas e precisa lidar com elas. Todas essas dependências são sobre o ajuste interno de acordo com a personalidade do seu sistema. Por exemplo, quando tentei usar sprintf (), que usa malloc () dentro. O Malloc possui o símbolo da função "t_sbrk" como um gancho no código, que deve ser implementado pelo usuário para impor as restrições de hardware. Aqui eu posso implementá-lo ou criar meu próprio malloc () se achar que posso fazer um melhor para o hardware incorporado, principalmente para outros usos, não apenas para o sprintf.
fonte