Je fais principalement du développement sur des appareils qui ont porté Linux, de sorte que la bibliothèque C standard fournit de nombreuses fonctionnalités grâce à la mise en œuvre d'appels système qui ont un comportement normalisé.
Cependant, pour le métal nu, il n'y a pas de système d'exploitation sous-jacent. Existe-t-il une norme relative à la façon dont la bibliothèque AC doit être implémentée ou devez-vous réapprendre la particularité des implémentations d'une bibliothèque lorsque vous passez à une nouvelle carte qui fournit un BSP différent?
Réponses:
Oui, il existe une norme, il suffit de la bibliothèque standard C . Les fonctions de la bibliothèque ne nécessitent pas de système d'exploitation «complet», ni aucun système d'exploitation, et il existe un certain nombre d'implémentations adaptées au code «nu», Newlib étant peut-être le plus connu.
Prenant Newlib comme exemple, cela vous oblige à écrire un petit sous-ensemble de fonctions de base, principalement comment les fichiers et l'allocation de mémoire sont gérés dans votre système. Si vous utilisez une plate-forme cible commune, il est probable que quelqu'un ait déjà fait ce travail pour vous.
Si vous utilisez linux (probablement aussi OSX et peut-être même cygwin / msys?) Et tapez
man strlen
, il devrait avoir une section appelée quelque chose commeCONFORMING TO
, qui vous dirait que l'implémentation est conforme à une norme spécifique. De cette façon, vous pouvez déterminer si quelque chose que vous avez utilisé est une fonction standard ou si cela dépend d'un système d'exploitation spécifique.la source
stdlib
implémentestdio
sans dépendre de l'OS. commefopen()
,fclose()
,fread()
,fwrite()
,putc()
etgetc()
? et commentmalloc()
fonctionne sans parler au système d'exploitation?getchar
etputchar
qui connaissent l'UART de votre matériel; puis des couches Newlibprintf
par dessus. Les E / S de fichiers s'appuieront également sur quelques primitives.stdin
etstdout
etstderr
(qui prend en chargeputchar()
etgetchar()
) qui dirige les E / S de / vers un UART, si votre plate-forme dispose d'un stockage de fichiers, comme avec un flash, vous devez également écrire de la colle pour cela. et vous devez avoir les moyens demalloc()
etfree()
. Je pense que si vous vous occupez de ces problèmes, vous pouvez à peu près exécuter le C portable dans votre cible intégrée (nonargv
niargc
).Tout d'abord, la norme C définit quelque chose appelé une implémentation "autonome", par opposition à une implémentation "hébergée" (ce que la plupart d'entre nous connaissent, la gamme complète des fonctions C prises en charge par le système d'exploitation sous-jacent).
Une implémentation "autonome" doit définir uniquement un sous-ensemble des en-têtes de bibliothèque C, à savoir ceux qui ne nécessitent pas de support, ou même la définition de fonctions (ils font simplement
#define
s ettypedef
s):<float.h>
<iso646.h>
<limits.h>
<stdalign.h>
<stdarg.h>
<stdbool.h>
<stddef.h>
<stdint.h>
<stdnoreturn.h>
Lorsque vous passez à l'étape suivante vers une implémentation hébergée, vous constaterez qu'il n'y a que très peu de fonctions qui ont vraiment besoin d'interfacer "le système" de quelque manière que ce soit, le reste de la bibliothèque pouvant être implémenté en plus de ces "primitives". ". En implémentant le PDCLib , j'ai fait quelques efforts pour les isoler dans un sous-répertoire séparé pour une identification facile lors du portage de la lib vers une nouvelle plateforme (exemples pour le port Linux entre parenthèses):
getenv()
(extern char * * environ
)system()
(fork()
/execve()
/wait()
)malloc()
etfree()
(brk()
/sbrk()
)_Exit()
(_exit()
)time()
(pas encore mis en œuvre)Et pour
<stdio.h>
(sans doute le plus "OS impliqué" des en-têtes C99):open()
)close()
)unlink()
)link()
/unlink()
)write()
)read()
)lseek()
)Certains détails de la bibliothèque sont facultatifs, la norme proposant simplement de les implémenter de manière standard mais ne faisant pas d'une telle implémentation une exigence.
La
time()
fonction peut légalement juste revenir(time_t)-1
si aucun mécanisme de chronométrage n'est disponible.Les gestionnaires de signaux décrits pour
<signal.h>
n'ont pas besoin d'être invoqués par autre chose qu'un appel àraise()
, il n'y a aucune exigence que le système envoie réellement quelque chose commeSIGSEGV
à l'application.L'en-tête C11
<threads.h>
, qui est (pour des raisons évidentes) très dépendant du système d'exploitation, n'a pas du tout besoin d'être fourni si l'implémentation définit__STDC_NO_THREADS__
...Il y a d'autres exemples, mais je ne les ai pas sous la main pour le moment.
Le reste de la bibliothèque peut être implémenté sans aucune aide de l'environnement. (*)
(*) Avertissement: la mise en œuvre de PDCLib n'est pas encore terminée, donc j'aurais peut-être oublié une chose ou deux. ;-)
la source
La norme C est en fait définie séparément de l'environnement d'exploitation. Aucune hypothèse n'est faite sur la présence d'un OS hôte, et les parties qui dépendent de l'hôte sont définies comme telles.
Autrement dit, la norme C est déjà assez de métal nu.
Bien sûr, les parties de langage que nous aimons tant, les bibliothèques, sont souvent là où le langage principal pousse à héberger des choses spécifiques. Par conséquent, le compilateur croisé "xxx-lib" typique trouvé pour de nombreux outils de plate-forme bare metal.
la source
Exemple exécutable minimal de Newlib
Ici, je fournis un exemple hautement automatisé et documenté qui montre newlib en action dans QEMU .
Avec newlib, vous implémentez vos propres appels système pour votre plateforme baremetal.
Par exemple, dans l'exemple ci-dessus, nous avons un exemple de programme
exit.c
:et dans un fichier C séparé
common.c
, nous implémentons leexit
avec ARM semihosting :Les autres appels système typiques que vous implémenterez sont:
write
pour afficher les résultats sur l'hôte. Cela peut être fait soit avec:brk
pourmalloc
.Facile sur baremetal, car nous n'avons pas à nous soucier de la pagination!
TODO Je me demande s'il est réaliste d'atteindre l'exécution de syscalls de planification préemptive sans entrer dans un RTOS complet comme Zephyr ou FreeRTOS .
La chose intéressante à propos de Newlib, c'est qu'il implémente toutes les choses non spécifiques au système d'exploitation comme
string.h
pour vous, et vous permet d'implémenter uniquement les talons du système d'exploitation.De plus, vous n'avez pas à implémenter tous les stubs, mais seulement ceux dont vous aurez besoin. Par exemple, si votre programme a seulement besoin
exit
, vous n'avez pas à fournir deprint
.L'arbre source de Newlib a déjà quelques implémentations, y compris une implémentation semi-hôte ARM sous
newlib/libc/sys/arm
, mais pour la plupart, vous devez implémenter la vôtre. Il fournit cependant une base solide pour la tâche.La façon la plus simple de configurer Newlib est de construire votre propre compilateur avec crosstool-NG, il vous suffit de lui dire que vous souhaitez utiliser Newlib comme bibliothèque C. Ma configuration gère cela automatiquement pour vous avec ce script , qui utilise les configurations newlib présentes sur
crosstool_ng_config
.Je pense que C ++ fonctionnera également, mais TODO le teste.
la source
Lorsque vous l'utilisez baremetal, vous découvrez des dépendances non implémentées et devez les gérer. Toutes ces dépendances concernent le réglage des internes en fonction de la personnalité de votre système. Par exemple, lorsque j'ai essayé d'utiliser sprintf () qui utilise malloc () à l'intérieur. Malloc a le symbole de fonction "t_sbrk" comme un crochet dans le code, qui doit être implémenté par l'utilisateur pour appliquer les contraintes matérielles. Ici, je peux l'implémenter, ou faire mon propre malloc () si je crois que je pourrais en faire un meilleur pour le matériel embarqué, principalement pour d'autres utilisations, pas seulement sprintf.
la source