x8664start_kernel()
startup_64 では x86_64_start_kernel() が呼ばれていたが、x86_64_start_kernel() では割り込みハンドラを仮設定したあとに start_kernel() を呼び出すことになる。start_kernel() では init プロセスが動作するまでのカーネル環境の初期化処理が行われる。
まずは x86_64_start_kernel() を軽く見てみる。ソースコードは arch/x86/kernel/head64.c となる。
void __init x86_64_start_kernel(char * real_mode_data)
{
... (中略) ...
/* clear bss before set_intr_gate with early_idt_handler */
clear_bss();
for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
set_intr_gate(i, early_idt_handlers[i]);
load_idt((const struct desc_ptr *)&idt_descr);
copy_bootdata(__va(real_mode_data));
/*
* Load microcode early on BSP.
*/
load_ucode_bsp();
if (console_loglevel == 10)
early_printk("Kernel alive\n");
clear_page(init_level4_pgt);
/* set init_level4_pgt kernel high mapping*/
init_level4_pgt[511] = early_level4_pgt[511];
x86_64_start_reservations(real_mode_data);
}
void __init x86_64_start_reservations(char *real_mode_data)
{
/* version is always not zero if it is copied */
if (!boot_params.hdr.version)
copy_bootdata(__va(real_mode_data));
reserve_ebda_region();
start_kernel();
}
start_kernel()
いよいよ start_kernel() だが、こちらは init/main.c に定義されており、前述の通り様々なコンポーネントに対して初期化処理が走る。
- CPU
- メモリ
- IO
- プロセススケジューラ
- 割り込み
…などなど。
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];
...
local_irq_disable(); //割り込み禁止
early_boot_irqs_disabled = true;
...
setup_arch(&command_line);
...
setup_log_buf(0); //カーネルのログバッファ初期化
pidhash_init(); //PID のハッシュテーブル初期化
vfs_caches_init_early(); // inode ハッシュテーブル初期化
sort_main_extable();
trap_init();
mm_init();
...
sched_init(); //ランキューの初期化
...
early_irq_init();
init_IRQ();
...
softirq_init();
timekeeping_init();
time_init();
...
early_boot_irqs_disabled = false;
local_irq_enable(); //割り込み許可
...
fork_init(totalram_pages); //タスク数上限の設定
...
signals_init(); // シグナルキューの作成
...
proc_root_init(); // proc fs のマウント
...
rest_init();
}
以下、いくつかのコンポーネントに対して軽く説明を記載する。
setup_arch()
setup_arch() は機種依存な初期化処理を行う関数であり、arch/x86/kernel/setup.c に定義されている。
いくつかの割り込みハンドラの登録やメモリマップやページングの設定などが行われる。
mm_init()
mm_init() は page 構造体とメモリアロケータの初期化を行う関数であり、init/main.c に定義されている。
実際にこれらの処理は内部で呼ばれている mem_init() (arch/x86/mm/init_64.c) で実行されており、コンソールに以下のようなメッセージを表示するのもこいつ。
Memory: 5742800k/15990784k available (6916k kernel code, 262540k absent, 534876k reserved, 4551k data, 1800k init)
これらの値がどこから来たのかは以下に記載されている。(arch/x86/mm/init_64.c)
void __init mem_init(void)
{
... (中略) ...
printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, "
"%ldk absent, %ldk reserved, %ldk data, %ldk init)\n",
nr_free_pages() << (PAGE_SHIFT-10),
max_pfn << (PAGE_SHIFT-10),
codesize >> 10,
absent_pages << (PAGE_SHIFT-10),
reservedpages << (PAGE_SHIFT-10),
datasize >> 10,
initsize >> 10);
}
absent_pages というのは、メモリホール (物理メモリ上の “使用できない” 領域のこと) のページ数であり、maxpfn はメモリホールを気にせず、とにかく最上位物理アドレスのページフレーム番号である。
15990784k (= 15.99 GB) というのは、maxpfn であり、これから absent_pages を引いた値が実メモリ量となるため、一見ちょっと多くメモリを持っているように見える。
(実際このインスタンスは c4.2xlarge であり、メモリは 15 GB)
trap_init()
現時点で IDT (Interrupt Descriptor Table) は、x86_64_start_kernel() により仮のテーブルが設定されている状態である。
trap_init() は、あらかじめカーネル内のデータ領域に静的に確保されている新しい IDT にゲートデスクリプタ* エントリを登録して初期化する処理を行う。arch/x86/kernel/traps.c に実装が定義されている。
init_IRQ()
init_IRQ() は、外部ハードウェアの割り込みコントローラ 8259A の初期化及びそれらに対する割り込みハンドラを IDT に登録する処理を行う。
arch/x86/kernel/irqinit.c に定義されている init_IRQ() は以下のように for ループでベクタ番号から IRQ 番号への変換テーブルを定義しており、この vector_irq[] という配列は do_IRQ() が IRQ 番号を得るために使用される。
void setup_vector_irq(int cpu)
{
#ifndef CONFIG_X86_IO_APIC
int irq;
for (irq = 0; irq < nr_legacy_irqs(); irq++)
per_cpu(vector_irq, cpu)[IRQ0_VECTOR + irq] = irq;
#endif
__setup_vector_irq(cpu);
}
timekeeping_init()
timekeeping_init() は RTC から現在時刻を取得し、システム時刻を保持する timekeeper 構造体 tk に設定する処理を行う。
実装は kernel/time/timekeeping.c に定義されている。
void __init timekeeping_init(void)
{
struct timekeeper *tk = &timekeeper;
struct clocksource *clock;
unsigned long flags;
struct timespec64 now, boot, tmp;
struct timespec ts;
...(中略)...
tk_set_xtime(tk, &now);
time_init()
time_init() は HPET (High Performance Event Timer) のタイマ割り込みの設定と、タイマ割り込みハンドラの登録、CPU クロック周波数の計算処理を行う。