xv6源码阅读
Talk is cheap. Show me the code.
调试篇
- 执行
make -nB qemu | vim -
,在vim中执行:set nowrap
,然后利用全局替换%s/ /\r /g
将所有的空格替换为换行加空格,即可提取出编译时的选项,包括库的依赖等等 .gdbinit
文件会在每次gdb时都会默认执行的命令,比如symbol-file
都可以放在里面。在用vscode调试时,要将target remote
注释掉,使用gdb调试时就恢复即可- sscratch中存的是trapframe的地址,在trampoline中与a0寄存器交换。最后会将sscratch中的a0的值赋给t0,然后存入对应a0在trapframe中的位置,然后再从trapframe中读取相关的内核信息(这一部分在trapframe中是不变的),比如读内核栈指针(将用户栈指针切换为内核栈指针)、读取coreid、读取系统调用的入口地址、读取内核页表地址然后和satp交换(用户页表切换为内核页表)。进入usertrap之后还需要保存用户的PC。
- 就像trap一样,需要一个trampoline来保存现场。因此进程之间的上下文切换,每个进程的proc中就需要有一个trapframe结构保存当前进程的上下文
启程
CPU
- 结构。
noff
指的是锁嵌套的层数,intena
指的是push_off
之前的中断状态,1表示开中断1
2
3
4
5
6struct cpu {
struct proc *proc; // The process running on this cpu, or null.
struct context context; // swtch() here to enter scheduler().
int noff; // Depth of push_off() nesting.
int intena; // Were interrupts enabled before push_off()?
};SpinLock
- 自旋锁底层通过test_and_set不断地轮询是否能获取锁(占用CPU资源)
- 结构
1
2
3
4
5
6
7
8// Mutual exclusion lock.
struct spinlock {
uint locked; // Is the lock held?
// For debugging:
char *name; // Name of lock.
struct cpu *cpu; // The cpu holding the lock.
}; - initlock初始化传入的参数lk锁
1
2
3
4
5
6
7void
initlock(struct spinlock *lk, char *name)
{
lk->name = name;
lk->locked = 0; // 1为持有锁
lk->cpu = 0; // 初始化为NULL
} - holding。检查是否当前CPU持有锁
1
2
3
4
5
6
7int
holding(struct spinlock *lk)
{
int r;
r = (lk->locked && lk->cpu == mycpu());
return r;
} - acquire。获取锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void
acquire(struct spinlock *lk)
{
push_off(); // 关中断并记录中断前的中断状态
if(holding(lk)) // 若当前CPU已经持有该自旋锁,则报错
panic("acquire");
// (amoswap, atomic memory operation:swap doubleword)原子双字交换
// On RISC-V, sync_lock_test_and_set turns into an atomic swap:
// a5 = 1
// s1 = &lk->locked
// amoswap.w.aq a5, a5, (s1)
while(__sync_lock_test_and_set(&lk->locked, 1) != 0) // 不断test直到锁未被持有(i.e. locked=0)结束while循环
;
// On RISC-V, this emits a fence instruction.
__sync_synchronize(); // 阻止编译器和处理器重排ld和st指令
lk->cpu = mycpu(); // 记录获取锁的CPU
} - release。释放锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void
release(struct spinlock *lk)
{
if(!holding(lk))
panic("release");
lk->cpu = 0; // 把持有锁的CPU置为NULL
// On RISC-V, this emits a fence instruction.
__sync_synchronize();
// Release the lock, equivalent to lk->locked = 0.
// This code doesn't use a C assignment, since the C standard
// implies that an assignment might be implemented with
// multiple store instructions.
// On RISC-V, sync_lock_release turns into an atomic swap:
// s1 = &lk->locked
// amoswap.w zero, zero, (s1)
__sync_lock_release(&lk->locked); // 原子交换,将lk->locked置为0,C函数赋值不是原子的(会调用很多个st指令)
pop_off(); // 减少锁的嵌套相关信息
} - push_off。记录锁的嵌套信息,必须得调用intr_off()关中断避免死锁
1
2
3
4
5
6
7
8
9
10
11void
push_off(void)
{
// 记录关中断前的中断状态
int old = intr_get(); // 获取状态寄存器的值判断中断使能位是否为1,返回使能位
intr_off(); // 关闭中断,对状态寄存器进行逻辑操作
if(mycpu()->noff == 0) // 如果无锁嵌套则将关中断前的中断状态记录到当前core中
mycpu()->intena = old;
mycpu()->noff += 1; // 当前core的锁嵌套数加1
} - pop_off。
1
2
3
4
5
6
7
8
9
10
11
12
13void
pop_off(void)
{
struct cpu *c = mycpu();
if(intr_get())
panic("pop_off - interruptible"); // 如果当前加锁状态下未关中断,则报错
if(c->noff < 1)
panic("pop_off"); // 如果未加锁嵌套状态下解锁,则报错
c->noff -= 1;
if(c->noff == 0 && c->intena) // 如果无锁嵌套,且在加锁之前是中断使能状态,则打开中断
intr_on();
}UART
- QEMU模拟的UART中的控制寄存器的访问是存储器映射I/O方式,在xv6物理地址空间中的起始地址是
0x10000000
其中包括8个I/O寄存器顺序从放在该地址起始处,具体寄存器值得设置参见 - uartinit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27void
uartinit(void)
{
// disable interrupts.
WriteReg(IER, 0x00);
// special mode to set baud rate.
WriteReg(LCR, LCR_BAUD_LATCH);
// LSB for baud rate of 38.4K.
WriteReg(0, 0x03);
// MSB for baud rate of 38.4K.
WriteReg(1, 0x00);
// leave set-baud mode,
// and set word length to 8 bits, no parity.
WriteReg(LCR, LCR_EIGHT_BITS);
// reset and enable FIFOs.
WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
// enable transmit and receive interrupts.
WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
initlock(&uart_tx_lock, "uart"); // 初始化uart串口的锁
}CONSOLE
- 结构
1
2
3
4
5
6
7
8
9
10struct {
struct spinlock lock;
// input
char buf[INPUT_BUF];
uint r; // Read index
uint w; // Write index
uint e; // Edit index
} cons; - consoleinit
1
2
3
4
5
6
7
8
9
10
11
12
13void
consoleinit(void)
{
initlock(&cons.lock, "cons"); // 初始化cons中的自旋锁
uartinit(); // 初始化I/O控制寄存器
// connect read and write system calls
// to consoleread and consolewrite.
// devsw结构体中存储着设备读写函数,devsw数组的索引为设备号,CONSOLE为1
devsw[CONSOLE].read = consoleread;
devsw[CONSOLE].write = consolewrite;
}