アドバンスドソフトウェア2015ソース読み会 xv6 †
xv6 を読めるようにした vagrant box があるのでそれを使います。
中身は Ubuntu 14.04 です
box のダウンロード †
でダウンロードします。
VM の起動 †
- vagrant init xv6
- vagrant up
で起動します。
VM への ssh †
で接続します。
VM の内部構成 †
ホームディレクトリに xv6 と xv6-64 があります
両方とも動きますが今回は xv6-64 を使います。
xv6 の起動 †
で起動します。
で gdb attach 待ちで起動します。
xv6 は単体で終了できないのでqemuを落とすことで終了します。(C-a x で qemu 終了)
gdb の attach †
xv6 は gdb 用の .gdbinit を自動で生成するようになっています。
ただ、 gdb は boot process の 16 -> 32 -> 64 bit の切り替えに対応していないので、切り替え次第違う gdb を用意して attach します。
- gdb-multiarch -x .gdbinit64
とすると attach できます。これは16 -> 32 用の設定です。
attach している間にもう一つ
- gdb-multiarch -x .gdbinit64-2
として 64 bit 用の gdb を上げます。
64 bit 用の gdb を上げたら 32bit 用の gdb で
します。そうすると32bit用gdbが panic するので終了します。
32bit用gdb を終了すると 64bit用gdb がattachされているはずです。
なお、64bit用gdb の attach に失敗することが結構あります。
その時は64bit用gdbを落として $ gdb-multiarch -x .gdbinit64-2 をもう一度やると繋がります。
64bit用gdbが attach できればあとは main などに break をかけて読めます。
boot process (16 -> 32) †
まずは頭から読むことに
boot process (32 -> 64) †
- protected mode に入ったので 32bit 命令(.code32)
- Register を初期化して call bootmain
- bootmain は kernal/bootmain.c
- bootmain は readseg して entry を取ってきてそれを実行する。
- readseg は 0x10000 に命令を展開する
- entry には header があって 0x1BADB002 ってマジックナンバーで識別してます
- entry本体は kernel/entry64.S にあります
- entry64 では page table を初期化してます
- CR4.PAE を立てて memory space を 64bit に
- EFER.LME を立てて 64bit命令に変更して
- entry64low へ ljmp
- これで64bit mode。ここから main に入ります
main †
- 基本的には初期化
- 全部読んだ訳じゃないので読んだところだけ
- uartearlyinit
- uart って chip の初期化。
- console かな
- kinit1
- kernel 用のメモリの確保
- memory 管理用の lock を作って freerange
- freerange は kfree する
- kfree したメモリ空間を list で確保
- kvmalloc
- acpiinit
- acpi ってもので hardware information を取ってくるみたいです
- 具体的には CPU の数
- CPU が複数あれば mpinit も呼ぶ
- seginit
- pinit
- process table 用の lock を作っておわり
syscall †
次に syscall を読もうということに
- kernel/syscall.c にあります
- syscall table は include/syscall.h にあります
- syscall は trap から呼ばれるみたいですね
- trap は alltraps から呼ばれます
- alltraps は kernel/alltraps.S にあって trap vector を設定してます
sys_wait †
具体的な syscall としてまずは sys_wait
- 本体は kernel/proc.c の wait()
- ptable を lock
- 子プロセスが残ってないかチェックして
- sleep()
- sleep は proc の state を SLEEPING にして sched()
- sched は interuppt が enable かどうかチェックして swtch()
- swtch は stack pointer を切り替えます
- これで実行されるプロセスが切り替えられる
scheduler †
sched を読んだので scheduler 本体を読む
sys_fork †
あと1つくらい syscall を読もう、ということで fork
- fork() は kernel/proc.c
- allocproc で ptable の空きを取ってきて
- copyuvm で memory をcopy
- copy on write はたぶんしてない
x86のdebug †
qemu
- /mnt/dalmore-home/one/src/xv6-rpi/src
- 最初にqemuを予め立ち上げる
- CtrlA-Xで脱出
- cd /mnt/dalmore-home/one/src/xv6-rpi/src
- /net/open/Linux/arm/gcc-arm-none-eabi-7-2017-q4-major/bin/arm-none-eabi-gdb kernel.elf
- 最初にqemuを予め立ち上げる
- (gdb) b _start
- (gdb) target remote :1234
- qemu再起動
make clean;make -f makefile-armclang qemu-debug
ソフトウェアシステム論xv6読み会 †