カーネル探検(start_kernel()の処理を途中まで見てみた)

# 概要
カーネル処理のstart_kernel()の処理を途中まで見てみた。


# 前回
[start_kernel()以前](http://qiita.com/saikoro-steak/items/60f5ab6cb9d0f825df25)


# 今回 start_kernel()からrest_init()の途中まで
start_kernelは様々なモジュールを初期化して、内容は盛り沢山。なので今回も自分が気になる箇所だけを切り取りしてみる。

## 処理概要
* start_kernel()
    - カーネル環境の初期化
    - sched_init() ランキューの初期化
    - fork_init() タスク数の上限の設定
    - signal_init() シグナルキューの作成

↓

* setup_arch()
    - 機種依存な初期化処理
    - initプロセス用のメモリ構造体に初期値を設定
    - ページテーブルの作成

↓

* mm_init()
    - page構造体とメモリアロケータの初期化

↓

* rest_init()
    - kernel_thread(kernel_init,...)
    - kernel_thread(kerneladd,...)
    - schdule()
    - cpu_idle()
 
## start_kernel()
前回の続きから。とりあえず、start_kernelに入った直後にレジスタをみてみた。

```
(gdb) i r
```
![startkernelスタック.png](https://qiita-image-store.s3.amazonaws.com/0/91729/01c6c292-3ee5-acd4-851c-5aabd690948d.png)

スタックポインタを見てみると現在、実行している処理はinit_thread_unionところのスタックを利用している。これは前回みたスタックを静的に定義していた箇所のこと。やはりinit(プロセス番号1)というより現在の処理(swapper)が利用するスタックということでよいはず。

## setup_arch()
start_kernel() → setup_arch()

initプロセス用のメモリ構造体に初期値を設定している

```c
init_mm.start_code = (unsigned long) _text;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
init_mm.brk = _brk_end;
```
メモ：
 \_textなどはリンカースクリプト(下記載)に記載されている。


```:カーネルリンカースクリプト(arch/x86/kernel/vmlinux.lds.S)
    SECTIONS
 84 {
 85 #ifdef CONFIG_X86_32
 86         . = LOAD_OFFSET + LOAD_PHYSICAL_ADDR;
 87         phys_startup_32 = startup_32 - LOAD_OFFSET;
 88 #else
 89         . = __START_KERNEL;
 90         phys_startup_64 = startup_64 - LOAD_OFFSET;
 91 #endif
 92 
 93         /* Text and read-only data */
 94         .text :  AT(ADDR(.text) - LOAD_OFFSET) {
 95                 _text = .;
 96                 /* bootstrapping code */
 97                 HEAD_TEXT
 98 #ifdef CONFIG_X86_32
 99                 . = ALIGN(PAGE_SIZE);
100                 *(.text.page_aligned)
101 #endif
102                 . = ALIGN(8);
103                 _stext = .;
```

メモ：
これがカーネルのリンカースクリプト。カーネルの実行ファイル(vmlinuz)を作成するときにこのリンカースクリプトをみてリンクするオブジェクトファイルの各セクションをまとめたり、そのセクションが仮想メモリ上でどのアドレスに配置されるのかが記載されている。利用するのはオブジェクトファイルをリンクする時。

## GDBで変数中身と/proc/iomemと/boot/System.mapを見比べてみた

setup_arch()の中の_textなどがどこのカーネルの仮想アドレスに配置されているのか調べてみた。

GDB

```gdb
(gdb) print /x _text
$20 = 0xffffffff81000000 <startup_64>
(gdb) print /x _etext
$21 = 0xffffffff81619285
(gdb) print /x _edata
$22 = 0xffffffff81a2ddc0
(gdb) print /x _brk_end
$23 = 0xffffffff81ee4000
```

メモ：
* 0x1000000がカーネルのエントリポイント(startup_64())でそれは**物理アドレス**
* 0xffffffff81000000 **仮想アドレス**にリンクされている。
* GDBで表示したアドレスと一致している

```bash

# cat /proc/iomem 
  000f0000-000fffff : System ROM
  00100000-dd7a173f : System RAM
  01000000-01619284 : Kernel code
  01619285-01a2ddbf : Kernel data
  01bc7000-01ee3fff : Kernel bss
  2b000000-350fffff : Crash kernel
```

* /boot/System.map-カーネルバージョン

```bash

  271 0000000001000000 A phys_startup_64
  272 ffffffff81000000 T _text
  273 ffffffff81000000 T startup_64
  274 ffffffff81000110 T secondary_startup_64
  275 ffffffff810001b0 T start_cpu0
  276 ffffffff810001c5 t bad_address

28218 ffffffff81618b60 T bad_from_user
28219 ffffffff81618b66 t bad_to_user
28220 ffffffff81619285 T _etext
28221 ffffffff81619288 T __start_notes
28222 ffffffff81619404 T __stop_notes
28223 ffffffff81619410 R __start___ex_table

62139 ffffffff81a2d560 D inet6_protos
62140 ffffffff81a2dd60 d ipv6_packet_offload
62141 ffffffff81a2ddc0 D _edata
62142 ffffffff81a2e000 D __vvar_beginning_hack
62143 ffffffff81a2e000 D __vvar_page
62144 ffffffff81a2e000 D jiffies

69959 ffffffff81ee30e0 b net_header
69960 ffffffff81ee30e8 b klist_remove_lock
69961 ffffffff81ee4000 B __brk_base
69962 ffffffff81ee4000 B __bss_stop
69963 ffffffff81ef4000 b .brk.shared_info_page_brk
69964 ffffffff81ef5000 b .brk.p2m_missing

```

# まとめ
start_kernel()はカーネル環境の様々な初期化をして最後にrest_init()を呼び出して終わる。