私はデバイスドライバとカーネルプログラミングを学んでいます。 Jonathan Corbetの本によると、デバイスドライバには機能がありませんmain()
。
だから、2つの質問があります。
main()
デバイスドライバに機能が必要ない理由は何ですか?- カーネル自体に
main()
機能がありますか?
誰でも私にこれを説明できますか?
答え1
ユーザー空間プログラムでは、main()
プログラムのエントリポイントです。libc初期化コードによって呼び出されるバイナリが実行されるとき。カーネルコードはlibcに依存する余裕はありません。なぜなら、libc自体はメモリ割り当て、I / O、プロセス管理などのためにカーネルシステムコールインターフェイスに依存しているからです。
main()
つまり、カーネルコードではと同じですstart_kernel()
。ブートローダによって呼び出されるカーネルイメージをロードしたら、それをメモリに抽出し、必要なハードウェアとメモリページングを設定します。start_kernel()
ほとんどのシステム設定を行い、最終的にinitプロセスを作成します。
Linuxカーネルモジュールのエントリポイントは、マクロを呼び出してカーネルに登録するinit関数ですmodule_init()
。登録されたモジュール初期化関数は次のようになります。カーネルコードによって呼び出されるdo_initcalls()
カーネルの起動中にこの関数を通過しました。
答え2
カーネルにはmain
機能がありません。 main
C言語の概念だ。カーネルはCとアセンブリ言語で書かれています。カーネルのエントリコードはアセンブリ言語で書かれています。
起動順序は次のように構成されます。
- BIOSは通常、ブートブロックデバイスからブートローダをロードします。最近人気のブートローダーはgrubです。
- GrubはカーネルイメージをRAMにロードし、初期ルートデバイス(
initrd
)を使用できます。次に、特定のアドレスでコードを実行します。 - カーネルイメージには、ファイルシステムモジュールやデバイスドライバなどのいくつかのカーネルモジュールがあります。カーネルイメージはファイルシステムモジュールを使用してルートファイルシステムをマウントします。これで、カーネルはディスクからすべてのカーネルモジュールをロードして実行できるようになりました。
- カーネルは初期化タスクを実行します。例:PCIバスを参照してすべてのPCIデバイスを見つけ、すべてのデバイスドライバを初期化します。
- 最後に、カーネルはプロセス0とプロセス1(プロセス
init
)を生成し、CPUコンテキストをリング0からリング3に切り替えてからinitプロセス(プロセスIDは1)を開始します。これでカーネルの起動が完了しました! - この
init
プログラムはすべての初期化スクリプトを実行します。すべてのサービスが開始されました。シェルといいます。ユーザーはログインできます。
このmain
関数はC関数です。実際、メインメソッドはCプログラムのエントリポイントではありません。 Cランタイムは以前に多くの関数を呼び出しましたmain
。 GCCにはコンストラクタと呼ばれる拡張機能があります。 「コンストラクタ」と宣言された関数はmain
。
たとえば、
/* This should not be used directly. Use block_init etc. instead. */
#define module_init(function, type) \
static void _attribute__((constructor)) do_qemu_init ## function(void) { \
register_module_init(function, type); \
}
このマクロは qemu プロジェクトで提供されます。
答え3
たとえば、main() 関数があります。アーチ/x86/boot/main.cリアルモードから保護モードに切り替えるためにシステムを準備するために使用されますが、他のアーキテクチャにはこれらのコードはありません。良いものがあります概要x86 プラットフォームで Linux カーネル 2.6.x ブートがどのように機能するか。本当に読む価値があります。
文書によるとLinuxカーネル開発を行う方法、Linuxカーネルは
これは標準Cライブラリに依存しないため、C標準の一部をサポートしないスタンドアロンC環境です。
ところで、C規格によると何をするのか?
スタンドアロン環境のプログラムが「メイン」機能を定義する必要があるかどうかは、実装によって定義されます。