UNIX v6原始碼分析除錯之二:單步除錯系統程式碼 main函式之 kinit1
阿新 • • 發佈:2019-01-10
環境搭建完成,該學習Main.c的main函數了。先貼上main函式的實現。
int main(void) { kinit1(end, P2V(4*1024*1024)); // phys page allocator kvmalloc(); // kernel page table mpinit(); // collect info about this machine lapicinit(); seginit(); // set up segments cprintf("\ncpu%d: starting xv6\n\n", cpu->id); picinit(); // interrupt controller ioapicinit(); // another interrupt controller consoleinit(); // I/O devices & their interrupts uartinit(); // serial port pinit(); // process table tvinit(); // trap vectors binit(); // buffer cache fileinit(); // file table iinit(); // inode cache ideinit(); // disk if(!ismp) timerinit(); // uniprocessor timer startothers(); // start other processors kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers() userinit(); // first user process // Finish setting up this processor in mpmain. mpmain(); }
這裡呼叫了很多初始化的函式。作業系統的初始化其實是很繁重的工作,很多時候也隱藏著各種知識點。
這麼多函式,貪多嚼不爛,一個一個函式進行除錯和學習吧。
首先是 kinit1 函式的實現。
kinit1(end, P2V(4*1024*1024)); // phys page allocator
void
kinit1(void *vstart, void *vend)
{
initlock(&kmem.lock, "kmem");
kmem.use_lock = 0;
freerange(vstart, vend);
}
kinit1 函式就是核心記憶體的初始化,把虛擬記憶體地址vstart到vend的地址進行初始化。
首先入參vstart為end,end的值是多少呢,又是怎麼來的呢?
extern char end[]; // first address after kernel loaded from ELF file
end是全域性變數,但是在Unix v6程式碼中只有宣告,沒有定義。先單步看看end的值是多少吧,如下圖:
我們知道核心的虛擬起始地址是0x80100000,這裡end的值為0x801126fc。通過一番查詢,發現end是ld連結器的內建變數。
這個變數用來表示elf檔案裝載到記憶體之後的起始地址。在Kernel.ld中對end變數進行賦值,這裡bss段載入完成之後即為end的地址。
.bss : { *(.bss) } PROVIDE(end = .);
執行objdump命令,可以看到.bss段起始地址0x8010b5a0+0x0000715c = 0x801126fc, 等於end的地址。
接著看freerange函式。
void
freerange(void *vstart, void *vend)
{
char *p;
p = (char*)PGROUNDUP((uint)vstart);
for(; p + PGSIZE <= (char*)vend; p += PGSIZE)
kfree(p);
}
這裡每次釋放PGSIZE(4K)位元組的大小。然後形成free的連結串列。這些free的連結串列即用來分配記憶體。
void
kfree(char *v)
{
struct run *r;
if((uint)v % PGSIZE || v < end || v2p(v) >= PHYSTOP)
panic("kfree");
// Fill with junk to catch dangling refs.
memset(v, 1, PGSIZE);
if(kmem.use_lock)
acquire(&kmem.lock);
r = (struct run*)v;
r->next = kmem.freelist;
kmem.freelist = r;
if(kmem.use_lock)
release(&kmem.lock);
}
kfree即把要釋放的記憶體初始化為1,同時把釋放的記憶體形成連結串列的形式。具體如下圖:
我這裡仍然有一個疑問,就是記憶體連結串列這樣表示有什麼好處呢?是為了操作方便還是以前並不流行使用點陣圖來表示,如果有理解的同學,可以告知一下。