xv6 Booting — QEMU, System Calls, Kernel Organization
697 words
3 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# xv6 Booting — QEMU, System Calls, Kernel Organization ## 🎯 Learning Objectives - Trace the xv6 boot sequence from power-on to shell - Implement a system call in xv6 - Understand xv6 kernel source organization - Explain trap handling in xv6 * * * ## 1. xv6 on QEMU ### 1.1 QEMU Emulation QEMU emulates a RISC-V virt...

xv6 Booting — QEMU, System Calls, Kernel Organization
🎯 Learning Objectives
- Trace the xv6 boot sequence from power-on to shell
- Implement a system call in xv6
- Understand xv6 kernel source organization
- Explain trap handling in xv6
1. xv6 on QEMU
1.1 QEMU Emulation
QEMU emulates a RISC-V virt machine with:
- CPU: RV64 (RISC-V 64-bit) with 4 harts (hardware threads)
- Memory: 128 MB of RAM at address 0x80000000
- Devices: UART (console), PLIC (interrupt controller), VirtIO disk
1.2 Boot Sequence Detail
(Diagram)
2. System Call Implementation
2.1 Flow of a System Call
(Diagram)
2.2 Adding a New System Call
Step 1: Add syscall number in
kernel/syscall.hc#define SYS_mysyscall 22 // Choose next available number
Step 2: Implement the function in
kernel/sysproc.ccuint64 sys_mysyscall(void) { int arg; argint(0, &arg); // Get first argument printf("mysyscall called with arg: %d\n", arg); return arg * 2; // Return something useful }
Step 3: Add entry in
kernel/syscall.ccextern uint64 sys_mysyscall(void); // In the syscalls array: [SYS_mysyscall] sys_mysyscall,
Step 4: Add user-space wrapper in
user/usys.plperlentry("mysyscall");
Step 5: Declare in
user/user.hcint mysyscall(int);
3. Trap Handling
3.1 Types of Traps
| Type | Cause | Handler |
|---|---|---|
| ecall | System call | usertrap() → syscall() |
| Exception | Page fault, illegal instruction | usertrap() → kill or fixup |
| Timer interrupt | Preemptive scheduling | usertrap() → yield() |
| Device interrupt | UART, disk I/O | devintr() → handle device |
3.2 Trap Flow
(Diagram)
4. Process Scheduling in xv6
4.1 Round Robin Scheduler
c// kernel/proc.c void scheduler(void) { struct proc *p; struct cpu *c = mycpu(); c->proc = 0; for (;;) { // Avoid deadlock by enabling interrupts intr_on(); for (p = proc; p < &proc[NPROC]; p++) { acquire(&p->lock); if (p->state == RUNNABLE) { // Switch to this process p->state = RUNNING; c->proc = p; swtch(&c->context, &p->context); // Process returned; clean up c->proc = 0; } release(&p->lock); } } }
4.2 Context Switch in xv6
The
swtch function saves callee-saved registers (ra, sp, s0-s11) to the current context and loads from the new context:asm# kernel/swtch.S swtch: sd ra, 0(a0) # Save return address sd sp, 8(a0) # Save stack pointer sd s0, 16(a0) # Save callee-saved regs ... ld ra, 0(a1) # Load new return address ld sp, 8(a1) # Load new stack pointer ld s0, 16(a1) # Load callee-saved regs ... ret # Jump to new context
5. 📝 Practice Questions
Q1: What is the first C function called during xv6 boot?Answer:main()inkernel/main.c. Before reaching main(), the assembly code inentry.ssets up the initial page table and stack. Q2: How does xv6 transition from user mode to kernel mode?Answer: The user program executes theecallinstruction, which triggers a trap. The CPU switches to supervisor mode, saves user registers to the trapframe, and jumps to the kernel's trap handler (stvec) which callsusertrap(). Q3: How does the xv6 scheduler prevent races when accessing process state?Answer: Each process has a spinlock (p->lock) that must be held when examining or changing process state. The scheduler acquires this lock before checkingp->state == RUNNABLEand releases it only after context switching to the process. Q4: What is the role ofuser_init()in xv6?Answer:user_init()creates the very first user process. It allocates a process slot, loads theinitcodebinary (a tiny program that execs/init), sets up the user address space, and marks the process as RUNNABLE. Q5: Why does the xv6 scheduler enable interrupts at the start of each iteration?Answer: To ensure that timer interrupts can occur. If interrupts were disabled, the timer interrupt would never fire, and processes would never be preempted. The scheduler briefly enables interrupts to allow pending interrupts to be delivered.
6. 🔗 Cross-References
- Week 1 - OS Structures: Dual-mode operation, system call interface
- Week 3 - CPU Scheduling: xv6 uses Round Robin
- Week 7 - Memory Management: xv6 page table setup Join Discord PreviousThreadsNextCPU Scheduling: FCFS, SJF