xv6源码阅读

main calls userinit, mpmain calls scheduler to start running processes

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
27
28
29
30
// Set up first user process.
void
userinit(void)
{
struct proc *p;
extern char _binary_initcode_start[], _binary_initcode_size[];

acquire(&ptable.lock);
p = allocproc();
initproc = p;
if((p−>pgdir = setupkvm()) == 0)
panic("userinit: out of memory?");
inituvm(p−>pgdir, _binary_initcode_start, (int)_binary_initcode_size);
p−>sz = PGSIZE;
memset(p−>tf, 0, sizeof(*p−>tf));
p−>tf−>cs = (SEG_UCODE << 3) | DPL_USER;
p−>tf−>ds = (SEG_UDATA << 3) | DPL_USER;
p−>tf−>es = p−>tf−>ds;
p−>tf−>ss = p−>tf−>ds;
p−>tf−>eflags = FL_IF;
p−>tf−>esp = PGSIZE;
p−>tf−>eip = 0; // beginning of initcode.S

safestrcpy(p−>name, "initcode", sizeof(p−>name));
p−>cwd = namei("/");

p−>state = RUNNABLE;

release(&ptable.lock);
}

userinit

  • 调用allocproc
    • 在proc table中找到一个空闲的slot,用来存放进程的proc struct
    • 找到空闲的slot后, 为proc的kernel thread分配kernel stack
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
27
28
29
30
31
32
33
34
35
36
37
38
39
// Per−CPU process scheduler.
// Each CPU calls scheduler() after setting itself up.
// Scheduler never returns. It loops, doing:
// − choose a process to run
// − swtch to start running that process
// − eventually that process transfers control
// via swtch back to the scheduler.
void
scheduler(void)
{
struct proc *p;

for(;;){
// Enable interrupts on this processor.
sti();

// Loop over process table looking for process to run.
acquire(&ptable.lock);
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p−>state != RUNNABLE)
continue;

// Switch to chosen process. It is the process’s job
// to release ptable.lock and then reacquire it
// before jumping back to us.
proc = p;
switchuvm(p);
p−>state = RUNNING;
swtch(&cpu−>scheduler, p−>context);
switchkvm();

// Process is done running for now.
// It should have changed its p−>state before coming back.
proc = 0;
}
release(&ptable.lock);

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Switch TSS and h/w page table to correspond to process p.
void
switchuvm(struct proc *p)
{
pushcli();
cpu−>gdt[SEG_TSS] = SEG16(STS_T32A, &cpu−>ts, sizeof(cpu−>ts)−1, 0);
cpu−>gdt[SEG_TSS].s = 0;
cpu−>ts.ss0 = SEG_KDATA << 3;
cpu−>ts.esp0 = (uint)proc−>kstack + KSTACKSIZE;
// setting IOPL=0 in eflags *and* iomb beyond the tss segment limit
// forbids I/O instructions (e.g., inb and outb) from user space
cpu−>ts.iomb = (ushort) 0xFFFF;
ltr(SEG_TSS << 3);
if(p−>pgdir == 0)
panic("switchuvm: no pgdir");
lcr3(V2P(p−>pgdir)); // switch to process’s address space
popcli();
}
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
// note: EMBRYO -> find unused slot, occupy the slot
enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };

// Per−process state
struct proc {
uint sz; // Size of process memory (bytes)
pde_t* pgdir; // Page table
char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state
int pid; // Process ID
struct proc *parent; // Parent process
struct trapframe *tf; // Trap frame for current syscall, note: save register?
struct context *context; // swtch() here to run process, note: address of proc address
void *chan; // If non−zero, sleeping on chan
int killed; // If non−zero, have been killed
struct file *ofile[NOFILE]; // Open files, note: file table?
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
};

// Process memory is laid out contiguously, low addresses first:
// text
// original data and bss
// fixed−size stack
// expandable heap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Saved registers for kernel context switches.
// Don’t need to save all the segment registers (%cs, etc),
// because they are constant across kernel contexts.
// Don’t need to save %eax, %ecx, %edx, because the
// x86 convention is that the caller has saved them.
// Contexts are stored at the bottom of the stack they
// describe; the stack pointer is the address of the context.
// The layout of the context matches the layout of the stack in swtch.S
// at the "Switch stacks" comment. Switch doesn’t save eip explicitly,
// but it is on the stack and allocproc() manipulates it.
struct context {
uint edi;
uint esi;
uint ebx;
uint ebp;
uint eip;
};

x86寄存器