xv6的设计trick(不断更新)
1、每个进程通过时钟中断出发trap.c中的
if(proc && proc->state == RUNNING && tf->trapno == T_IRQ0+IRQ_TIMER)
yield();来强制把这个进程置为Runnable状态,yield的实现如下:
acquire(&ptable.lock); //DOC: yieldlock
proc->state = RUNNABLE;
sched();
release(&ptable.lock);
在sched中
swtch(&proc->context, cpu->scheduler); 实现从当前进程的kernel Stack 切换到 CPU中专门负责程序调度的 scheduler Stack,
(gdb) p /x *cpus[0]->scheduler
$7 = {edi = 0x0, esi = 0x80104ea4, ebx = 0x8, ebp = 0x8010c608,
eip = 0x80104ab2}
此时,我们在 sched设置断点
(gdb)b proc.c:314
(gdb)s 进入汇编代码
在swtch的汇编代码中,我们将当前进程的esp所指向的内存地址+8 作为新的上下文的esp,然后保存老寄存值,popl加载新的寄存器的值
swtch:
movl %esp, (%eax)
movl %edx, %esp
edx 的值就是指向 CPU scheduler的栈底指针的值
2、磁盘文件块读写
struct buf* bread(uint dev, uint blockno) {
struct buf* b;
b = bget(dev, blockno);
if (!(b.flags & B_VALID)) {
iderw(b);
}
return b;
}
static struct buf* bget(uint dev, uint blockno) {
struct buf* b;
acquire(&bcache.lk);
loop:
for(b = bcache.head.next; b != &bcache.head; b = b->next) {
//if the buf is already in the buffer
if (b->dev == dev && b->blockno == blockno) {
if (!(b.flags & B_BUSY)) {
b.flags &= B_BUSY;
release(&bcache.lk);
return b;
}
sleep(b, &bcache.lk);
goto loop;
}
}
for (b = bcache.head.prev; b != &bcache.head; b = b->next) {
if((b.flags & B_BUSY) == 0 && (b.flags & B_DIRTY) == 0) {
b->dev = dev;
b->blockno = blockno;
b->flags = B_BUSY;
release(&bcache.lk);
return b;
}
}
panic("there isn't any empty buf");
}