私は主にLinuxを移植したデバイスで開発を行っているため、標準Cライブラリは、標準化された動作を持つシステムコールを実装することにより、多くの機能を提供します。
ただし、ベアメタルの場合、基盤となるOSはありません。acライブラリの実装方法に関連する標準はありますか?または、異なるBSPを提供する新しいボードに切り替えるときにライブラリ実装の特性を再学習する必要がありますか?
私は主にLinuxを移植したデバイスで開発を行っているため、標準Cライブラリは、標準化された動作を持つシステムコールを実装することにより、多くの機能を提供します。
ただし、ベアメタルの場合、基盤となるOSはありません。acライブラリの実装方法に関連する標準はありますか?または、異なるBSPを提供する新しいボードに切り替えるときにライブラリ実装の特性を再学習する必要がありますか?
回答:
はい、標準、単にC標準ライブラリがあります。ライブラリ関数は「完全な」OS、またはOSをまったく必要としません。また、「ベアメタル」コードに合わせた多くの実装があります。Newlibは おそらく最もよく知られています。
Newlibを例にとると、主にシステム内でファイルとメモリの割り当てを処理する方法など、コア機能の小さなサブセットを記述する必要があります。共通のターゲットプラットフォームを使用している場合、誰かがこの仕事をすでに行っている可能性があります。
Linux(おそらくOSXも、おそらくcygwin / msysでも)とtype man strlen
を使用している場合CONFORMING TO
、のようなセクションが必要です。これにより、実装が特定の標準に準拠していることがわかります。このようにして、使用しているものが標準機能であるか、特定のOSに依存しているかを判断できます。
stdlib
実装する方法について興味stdio
があります。以下のようにfopen()
、fclose()
、fread()
、fwrite()
、putc()
とgetc()
?またmalloc()
、OS と通信せずにどのように機能しますか?
getchar
and putchar
; 次にprintf
、これらの上にNewlibレイヤーがあります。ファイルI / Oも同様にいくつかのプリミティブに依存します。
stdin
とstdout
し、stderr
(の世話をとるputchar()
と、getchar()
お使いのプラットフォームは、ファイルストレージを持っている場合は/からUARTへのI / Oを指示している)、フラッシュと同じように、あなたはまた、そのための書き込み糊を持っています。あなたがするための手段を持っている必要がありますmalloc()
とfree()
。これらの問題を処理すれば、埋め込みターゲットでポータブルCを実行できると思います(いいえargv
もなしargc
)。
acライブラリの実装方法に関連する標準はありますか?または、異なるBSPを提供する新しいボードに切り替えるときにライブラリ実装の特性を再学習する必要がありますか?
まず、C標準では、「ホスト型」実装とは対照的に「自立型」実装と呼ばれるものを定義しています(これは、私たちの多くが知っている、基盤となるOSがサポートするC関数の全範囲です)。
「自立型」実装では、Cライブラリヘッダーのサブセット、つまりサポートを必要としないもの、または関数の定義(#define
sとtypedef
s のみを行う)さえ定義する必要があります。
<float.h>
<iso646.h>
<limits.h>
<stdalign.h>
<stdarg.h>
<stdbool.h>
<stddef.h>
<stdint.h>
<stdnoreturn.h>
ホストされた実装に向けて次の一歩を踏み出すと、「システム」とのインターフェースを実際に必要とする関数は非常に少なく、残りのライブラリはそれらの「プリミティブ」の上に実装可能であることがわかります。 「。PDCLibの実装では、libを新しいプラットフォームに移植するときに簡単に識別できるように、別のサブディレクトリにそれらを分離するための努力をしました(括弧内のLinuxポートの例):
getenv()
(extern char * * environ
)system()
(fork()
/ execve()
/ wait()
)malloc()
およびfree()
(brk()
/ sbrk()
)_Exit()
(_exit()
)time()
(まだ実装されていません)そして<stdio.h>
(おそらく、最も「OSに関係する」C99ヘッダー):
open()
)close()
)unlink()
)link()
/ unlink()
)write()
)read()
)lseek()
)ライブラリの特定の詳細はオプションであり、標準は単に標準的な方法で実装することを提供するだけであり、そのような実装を要件にしません。
time()
機能は、法的にだけ返すことが(time_t)-1
何の計時メカニックが利用できない場合。
記載されているシグナルハンドラは<signal.h>
、への呼び出し以外で呼び出す必要はありません。raise()
システムが実際にアプリケーションに何かを送信する必要はありませんSIGSEGV
。
<threads.h>
(明らかな理由で)OS に非常に依存しているC11ヘッダーは、実装で定義されている場合、まったく提供する必要はありません__STDC_NO_THREADS__
...
他にも例はありますが、今は手元にありません。
ライブラリの残りの部分は、環境の助けなしに実装できます。(*)
(*)警告:PDCLibの実装はまだ完全ではないので、1つまたは2つを見落としているかもしれません。;-)
標準Cは、実際にはオペレーティング環境とは別に定義されます。ホストOSが存在するという仮定は行われず、ホストに依存する部分はそのように定義されます。
つまり、C標準はすでにかなりベアメタルです。
もちろん、私たちが大好きな言語の部分であるライブラリは、多くの場合、コア言語が特定のホストをホストする場所です。したがって、多くのベアメタルプラットフォームツールに見られる典型的な「xxx-lib」クロスコンパイラーです。
Newlib最小実行可能サンプル
ここでは、QEMUでのnewlibの動作を示す高度に自動化され、文書化された例を提供します。
newlibを使用して、ベアメタルプラットフォーム用の独自のシステムコールを実装します。
たとえば、上記の例では、プログラム例がありますexit.c
:
#include <stdio.h>
#include <stdlib.h>
void main(void) {
exit(0);
}
そして、別のCファイルにはcommon.c
、我々は実装exit
してARMはセミホスティング:
void _exit(int status) {
__asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456");
}
実装する他の典型的なシステムコールは次のとおりです。
write
結果をホストに出力します。これは次のいずれかで実行できます。
brk
のためにmalloc
。
ページングを気にする必要がないため、ベアメタルで簡単です!
TODO ZephyrやFreeRTOSなどの本格的なRTOSに移行せずに、プリコールスケジューリングのシステムコールの実行に到達するのが現実的かどうか疑問に思います。
Newlibのすばらしい点は、OS固有ではないものをすべてstring.h
実装し、OSスタブだけを実装できることです。
また、すべてのスタブを実装する必要はなく、必要なスタブのみを実装する必要があります。たとえば、プログラムで必要なのがのみの場合exit
、を提供する必要はありませんprint
。
NewlibソースツリーにはすでにARMセミホスティング実装を含むいくつかの実装newlib/libc/sys/arm
がありますが、ほとんどの場合、独自の実装が必要です。ただし、タスクの基盤はしっかりしています。
Newlibをセットアップする最も簡単な方法は、crosstool-NGを使用して独自のコンパイラを構築することです。NewlibをCライブラリとして使用することを伝えるだけです。私のセットアップでは、このスクリプトを使用して自動的に処理しますcrosstool_ng_config
。
C ++も動作すると思いますが、TODOでテストします。
ベアメタルで使用する場合、未実装の依存関係をいくつか発見し、それらを処理する必要があります。これらの依存関係はすべて、システムの性格に応じて内部を調整することに関するものです。たとえば、内部でmalloc()を使用するsprintf()を使用しようとしたとき。Mallocには、コード内のフックとして「t_sbrk」関数シンボルがあります。これは、ハードウェアの制約を強制するためにユーザーが実装する必要があります。ここで、それを実装するか、sprintfだけでなく、主に他の用途のために組み込みハードウェアに対してより良いものを作成できると思う場合は、独自のmalloc()を作成します。