OS起動編① kinit1(end, P2V(4*1024*1024)) (Xv6を読む~OSコードリーディング~)
前回
jupiteroak.hatenablog.com
トップページ
jupiteroak.hatenablog.com
main.c(一部抜粋)
https://github.com/mit-pdos/xv6-public/blob/master/main.c#L20
int main(void) { kinit1(end, P2V(4*1024*1024)); // phys page allocator ...
kalloc.c
https://github.com/mit-pdos/xv6-public/blob/master/kalloc.c#L31
void kinit1(void *vstart, void *vend) { initlock(&kmem.lock, "kmem"); kmem.use_lock = 0; freerange(vstart, vend); }
Unix xv6では、カーネル空間のメモリ領域0x8011 5000〜0x8E00 0000をメモリ割り当てのために使用します。
そのための準備として、kinit1関数を呼び出し、0x8011 5000から0x8040 0000までのメモリ領域約4MBを使用可能な状態として管理します(使用可能なメモリページを把握するためのリストを作成します)。ここで使用可能な状態にしたメモリ領域約4MBは、次のkvmalloc関数の処理において、ページディレクトリ、ページテーブルを作成するために使用します。
処理の内容
initlock(&kmem.lock, "kmem");
initlock関数を呼び出して、メモリ管理のために使用するkmem構造体(使用可能なメモリページを把握するために用いられるリスト)に関わるロックを初期化しています。
kmem.use_lock = 0;
kmem構造体のロックをこれから使用する場合は1を、そうではない場合は0を設定します。
freerange(vstart, vend);
freerange関数を呼び出して、引数vstartで指定された仮想アドレスend→0x8011 44a8(freerange関数内で0x8011 5000に切り上げられる)から、引数vendで指定された仮想アドレスP2V(4*1024*1024)→0x8040 0000までのメモリ領域を使用可能な状態として管理します(使用可能なメモリページを把握するためのリストを作成します)。
endは、リンカスクリプトkernel.ldで定義されたシンボル(ラベル)で、メインメモリにロードされたカーネルプログラムにおけるbss領域の終端アドレスです。
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) ENTRY(_start) SECTIONS { /* Link the kernel at this address: "." means the current address */ /* Must be equal to KERNLINK */ . = 0x80100000; .text : AT(0x100000) { *(.text .stub .text.* .gnu.linkonce.t.*) } PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } /* Include debugging information in kernel memory */ .stab : { PROVIDE(__STAB_BEGIN__ = .); *(.stab); PROVIDE(__STAB_END__ = .); } .stabstr : { PROVIDE(__STABSTR_BEGIN__ = .); *(.stabstr); PROVIDE(__STABSTR_END__ = .); } /* Adjust the address for the data segment to the next page */ . = ALIGN(0x1000); /* Conventionally, Unix linkers provide pseudo-symbols * etext, edata, and end, at the end of the text, data, and bss. * For the kernel mapping, we need the address at the beginning * of the data section, but that's not one of the conventional * symbols, because the convention started before there was a * read-only rodata section between text and data. */ PROVIDE(data = .); /* The data segment */ .data : { *(.data) } PROVIDE(edata = .); .bss : { *(.bss) } PROVIDE(end = .); /DISCARD/ : { *(.eh_frame .note.GNU-stack) } }
リンカスクリプトで定義されたシンボルは、ソースコード内でextern char end[] を用いる事で参照できます。
kalloc.c
extern char end[]; // first address after kernel loaded from ELF file // defined by the kernel linker script in kernel.ld
その仮想アドレスの値は、0x8011 44a8です。
readelf -s kernel 286: 801144a8 0 NOTYPE GLOBAL DEFAULT 6 end
xv6実装の詳解(boot処理編: segmentationとpagingを中心に) - Qiita
→https://gist.github.com/knknkn1162/09e9a8c12e0a4ea0db07deb1d7bb4c19#file-kernel_readelf_s-log-L291より引用