GNU/Linux >> Linux の 問題 >  >> Linux

Linux カーネルにはメイン機能がありますか?

start_kernel

4.2 では、start_kernel init/main.c から かなりの初期化プロセスであり、 main と比較できます 関数。

これは最初に実行されるアーキテクチャに依存しないコードであり、カーネルの大部分をセットアップします。 main のように 、 start_kernel いくつかの下位レベルのセットアップ コードが先行します (crt* で行われます)。 ユーザーランド main のオブジェクト )、その後、「メイン」の汎用 C コードが実行されます。

どのように start_kernel x86_64 で呼び出される

arch/x86/kernel/vmlinux.lds.S 、リンカー スクリプト、セット:

ENTRY(phys_startup_64)

そして

phys_startup_64 = startup_64 - LOAD_OFFSET;

そして:

#define LOAD_OFFSET __START_KERNEL_map

arch/x86/include/asm/page_64_types.h __START_KERNEL_map を定義 として:

#define __START_KERNEL_map  _AC(0xffffffff80000000, UL)

これはカーネルエントリアドレスです。 TODO そのアドレスに正確に到達するにはどうすればよいですか? Linux がブートローダーに公開するインターフェースを理解する必要があります。

arch/x86/kernel/vmlinux.lds.S 最初のブートローダ セクションを次のように設定します:

.text :  AT(ADDR(.text) - LOAD_OFFSET) {
    _text = .;
    /* bootstrapping code */
    HEAD_TEXT

include/asm-generic/vmlinux.lds.h HEAD_TEXT を定義 :

#define HEAD_TEXT  *(.head.text)

arch/x86/kernel/head_64.S startup_64 を定義 .これは、実行される最初の x86 カーネル コードです。 たくさん セグメンテーションとページングを含む低レベルのセットアップの。

ファイルが次で始まるため、これが最初に実行されます:

.text
__HEAD
.code64
.globl startup_64

include/linux/init.h __HEAD を定義 として:

#define __HEAD      .section    ".head.text","ax"

リンカー スクリプトの最初の部分と同じです。

最後に x86_64_start_kernel を呼び出します lretq と少しぎこちない :

movq    initial_code(%rip),%rax
pushq   $0      # fake return address to stop unwinder
pushq   $__KERNEL_CS    # set correct cs
pushq   %rax        # target address in negative space
lretq

そして:

.balign 8
GLOBAL(initial_code)
.quad   x86_64_start_kernel

arch/x86/kernel/head64.c x86_64_start_kernel を定義 x86_64_start_reservations を呼び出す start_kernel を呼び出す .

arm64 エントリ ポイント

v5.7 非圧縮カーネルで実行される最初の arm64 は、https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L72 で定義されているため、add x13, x18, #0x16 または b stext CONFIG_EFI による :

    __HEAD
_head:
    /*
     * DO NOT MODIFY. Image header expected by Linux boot-loaders.
     */
#ifdef CONFIG_EFI
    /*
     * This add instruction has no meaningful effect except that
     * its opcode forms the magic "MZ" signature required by UEFI.
     */
    add x13, x18, #0x16
    b   stext
#else
    b   stext               // branch to kernel start, magic
    .long   0               // reserved
#endif
    le64sym _kernel_offset_le       // Image load offset from start of RAM, little-endian
    le64sym _kernel_size_le         // Effective size of kernel image, little-endian
    le64sym _kernel_flags_le        // Informative flags, little-endian
    .quad   0               // reserved
    .quad   0               // reserved
    .quad   0               // reserved
    .ascii  ARM64_IMAGE_MAGIC       // Magic number
#ifdef CONFIG_EFI
    .long   pe_header - _head       // Offset to the PE header.

これは、圧縮されていないカーネル イメージの最初のバイトでもあります。

どちらの場合も stext にジャンプします これにより、「実際の」アクションが開始されます。

コメントで述べたように、これら 2 つの命令は、https://github.com/cirosantilli/linux/blob/v5.7/Documentation/arm64/booting.rst#4-call で説明されている文書化されたヘッダーの最初の 64 バイトです。 -カーネルイメージ

arm64 の最初の MMU 対応命令:__primary_switched

__primary_switchedだと思います 頭の中.S:

/*
 * The following fragment of code is executed with the MMU enabled.
 *
 *   x0 = __PHYS_OFFSET
 */
__primary_switched:

この時点で、カーネルはページ テーブルを作成しているように見えますが、PC アドレスが vmlinux ELF ファイルのシンボルと一致するように自身を再配置する可能性があります。したがって、この時点で特別な魔法を使わなくても、GDB で意味のある関数名を確認できるはずです。

arm64 セカンダリ CPU エントリ ポイント

secondary_holding_pen 定義:https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691

エントリ手順について詳しくは、https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691

をご覧ください。

main() で あなたはおそらく main() を意味しています はプログラム、つまりその「エントリ ポイント」です。

init_module() のモジュールの場合 .

Linux デバイス ドライバーの第 2 版から:

<ブロック引用>

アプリケーションが最初から最後まで単一のタスクを実行するのに対して、モジュールは将来の要求に対応するために自身を登録し、その「メイン」機能はすぐに終了します。つまり、関数 init_module (モジュールのエントリ ポイント) のタスクは、後でモジュールの関数を呼び出す準備をすることです。モジュールが「私はここにいる、これが私にできることだ」と言っているようなものです。モジュールの 2 番目のエントリ ポイントである cleanup_module は、モジュールがアンロードされる直前に呼び出されます。カーネルに「私はもうそこにいません。他に何もするように頼まないでください。」と伝える必要があります。


基本的に、main() という名前のルーチンは特別なことではありません。 .上記のように、main() 実行可能なロード モジュールのエントリ ポイントとして機能します。ただし、ロード モジュールには異なるエントリ ポイントを定義できます。実際、お気に入りの dll を参照するなど、複数のエントリ ポイントを定義できます。

オペレーティング システム (OS) の観点から、実際に必要なのは、デバイス ドライバーとして機能するコードのエントリ ポイントのアドレスだけです。デバイス ドライバーがデバイスへの I/O を実行する必要がある場合、OS はそのエントリ ポイントに制御を渡します。

システム プログラマは、デバイス間の接続、デバイスのドライバとして機能するロード モジュール、およびロード モジュール内のエントリ ポイントの名前を定義します (各 OS には独自の方法があります)。

各 OS には独自のカーネルがあり (明らかに)、一部は main() で始まる場合があります。 しかし、main() を使用するカーネルを見つけたら驚くでしょう。 UNIX などの単純なもの以外では!カーネル コードを作成する頃には、作成するすべてのモジュールに main() という名前を付けるという要件をはるかに超えています。 .

これが役に立てば幸いです?

Unix バージョン 6 のカーネルからこのコード スニペットを見つけました。ご覧のとおり main() は、開始しようとしている別のプログラムです!

main()
{
     extern schar;
     register i, *p;
     /*
     * zero and free all of core
     */

     updlock = 0;
     i = *ka6 + USIZE;
     UISD->r[0] = 077406;
     for(;;) {
        if(fuibyte(0) < 0) break;
        clearsig(i);
        maxmem++;
        mfree(coremap, 1, i);
         i++;
     }
     if(cputype == 70) 
     for(i=0; i<62; i=+2) {
       UBMAP->r[i] = i<<12;
       UBMAP->r[i+1] = 0;
      }

    // etc. etc. etc.

いくつかの見方:

<オール>
  • デバイス ドライバはプログラムではありません。これらは、別のプログラム (カーネル) にロードされるモジュールです。そのため、main() はありません。 関数。

  • すべてのプログラムに main() が必要であるという事実 function はユーザー空間アプリケーションにのみ当てはまります。カーネルにもデバイス ドライバーにも適用されません。


  • Linux
    1. Linuxで元に戻すことはできますか?

    2. Linux –カーネル:名前空間のサポート?

    3. Linux – Linuxは、物理的にインストールしたメモリよりも多くのメモリと少ないメモリの両方を表示するのはなぜですか?

    1. Linux –カーネルIP転送?

    2. Linux – Linuxカーネルはマイクロカーネルアーキテクチャとどのように比較されますか?

    3. Linuxカーネルコードで__initはどういう意味ですか?

    1. Linux –異なるLinux / UNIXカーネルは交換可能ですか?

    2. returnステートメントのないメイン関数が値12を返すのはなぜですか?

    3. Linuxにはどのようなプロセスキラーがありますか?