环境搭建完成,该学习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 allocatorvoidkinit1(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函数。
voidfreerange(void *vstart, void *vend){ char *p; p = (char*)PGROUNDUP((uint)vstart); for(; p + PGSIZE <= (char*)vend; p += PGSIZE) kfree(p);}
这里每次释放PGSIZE(4K)字节的大小。然后形成free的链表。这些free的链表即用来分配内存。
voidkfree(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,同时把释放的内存形成链表的形式。具体如下图:
我这里仍然有一个疑问,就是内存链表这样表示有什么好处呢?是为了操作方便还是以前并不流行使用位图来表示,如果有理解的同学,可以告知一下。