Xv6を読む~OSコードリーディング~ ②概要

前回
jupiteroak.hatenablog.com




目次

ソースコードリーディングの概要

Xv6のソースコードのうち、ブートローダ→OS起動→プロセス1実行 までの処理について説明します。

ブートローダ

1.bootasm.S ← ここからスタート
2.bootmain.c

プロセス1実行編

1.forkret()
2.trapret
3.initcode.S
4.vectors.S
5.alltraps
6.trap(struct trapframe *tf);
7.syscall();
8.sys_exec();
9.exec(path, argv);
10.trapret
11.init.c ←ゴール

ソースコードリーディングの概要(補足説明)

ブートローダ

bootasm.S と bootmain.c がブートローダに該当するプログラムであることは、Makefileから判断できます。

Makefile(一部抜粋)
https://github.com/mit-pdos/xv6-public/blob/master/Makefile#L103

bootblock: bootasm.S bootmain.c
	$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
	$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
	$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
	$(OBJDUMP) -S bootblock.o > bootblock.asm
	$(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
	./sign.pl bootblock

bootasm.S と bootmain.c を使ってbootblockを作成し、最後に、作成したbootblockを引数にしてperlスクリプトsign.plを実行しています。

sign.pl
https://github.com/mit-pdos/xv6-public/blob/master/sign.pl

#!/usr/bin/perl

open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";

$n = sysread(SIG, $buf, 1000);

if($n > 510){
  print STDERR "boot block too large: $n bytes (max 510)\n";
  exit 1;
}

print STDERR "boot block is $n bytes (max 510)\n";

$buf .= "\0" x (510-$n);
$buf .= "\x55\xAA";

open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
print SIG $buf;
close SIG;

このsign.pl内の処理で、0x55、0xAAのマジックナンバーをbootblockに書き込んでいることから、bootblockがブートセクタであること→bootasm.S と bootmain.c がブートローダに該当するプログラムがであることがわかります。

OS起動編

ブートローダによるxv6カーネルのロードが終わると、xv6カーネル内にあるプログラムentry.Sから処理が始まり、続いてプログラムmain.cにあるmain関数に制御が移ります。このmain関数内で、OSの起動・初期化で必要となる関数を呼び出しています。

main.c
https://github.com/mit-pdos/xv6-public/blob/master/main.c#L17

// Bootstrap processor starts running C code here.
// Allocate a real stack and switch to it, first
// doing some setup required for memory allocator to work.
int
main(void)
{
  kinit1(end, P2V(4*1024*1024)); // phys page allocator
  kvmalloc();      // kernel page table
  mpinit();        // detect other processors
  lapicinit();     // interrupt controller
  seginit();       // segment descriptors
  picinit();       // disable pic
  ioapicinit();    // another interrupt controller
  consoleinit();   // console hardware
  uartinit();      // serial port
  pinit();         // process table
  tvinit();        // trap vectors
  binit();         // buffer cache
  fileinit();      // file table
  ideinit();       // disk 
  startothers();   // start other processors
  kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
  userinit();      // first user process
  mpmain();        // finish this processor's setup
}

プロセス1実行編

前述のmain関数内で呼び出されているuserinit関数によってプロセス1が作成され、そのプロセス1がスケジューリングされた後、プロセス1の実行が始まります。プロセス1に該当するプログラムはinitcode.Sです。プロセス1(initcode.S)はexecシステムコールを呼び出して、init.cをロード・実行します。

その他

主に、ブートローダ→OS起動→プロセス1実行 までの処理で使用される関数・マクロについて説明しています。こちらを先に読む必要はありません。


defs.h
#define NELEM(x)


x86.h
uchar inb(ushort port)
void insl(int port, void *addr, int cnt)
void outb(ushort port, uchar data)
void outw(ushort port, ushort data)
void outsl(int port, const void *addr, int cnt)
void stosb(void *addr, int data, int cnt)
void stosl(void *addr, int data, int cnt)
void lgdt(struct segdesc *p, int size)
void ltr(ushort sel)
void lcr3(uint val)
void lidt(struct gatedesc *p, int size)
uint readeflags(void)
void cli(void)
void sti(void)
uint xchg(volatile uint *addr, uint newval)


string.c
void* memset(void *dst, int c, uint n)
int memcmp(const void *v1, const void *v2, uint n)
void* memmove(void *dst, const void *src, uint n)
void* memcpy(void *dst, const void *src, uint n)
int strncmp(const char *p, const char *q, uint n)
char* strncpy(char *s, const char *t, int n)
char* safestrcpy(char *s, const char *t, int n)
int strlen(const char *s)


bootmain.c
void readseg(uchar* pa, uint count, uint offset)
void readsect(void *dst, uint offset)
void waitdisk(void)


asm.h
#define SEG_NULLASM
#define SEG_ASM(type,base,lim)


mmu.h
#define SEG16(type, base, lim, dpl)
#define SEG(type, base, lim, dpl)
#define PDX(va)
#define PTX(va)
#define PGADDR(d, t, o)
#define PTE_ADDR(pte)
#define PTE_FLAGS(pte)
#define PGROUNDUP(sz)
#define PGROUNDDOWN(a)
#define SETGATE(gate, istrap, sel, off, d)


memlayout.h
#define V2P(a)
#define P2V(a)
#define V2P_WO(x)
#define P2V_WO(x)


kalloc.c
void freerange(void *vstart, void *vend)
void kfree(char *v)
char* kalloc(void)


vm.c
pde_t* setupkvm(void)
void switchkvm(void)
void inituvm(pde_t *pgdir, char *init, uint sz)
int loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
pde_t* copyuvm(pde_t *pgdir, uint sz)
char* uva2ka(pde_t *pgdir, char *uva)
void clearpteu(pde_t *pgdir, char *uva)
void switchuvm(struct proc *p)
void freevm(pde_t *pgdir)
int allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
int deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)
int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
pte_t * walkpgdir(pde_t *pgdir, const void *va, int alloc)


mp.c
struct mpconf* mpconfig(struct mp **pmp)
struct mp* mpsearch(void)
struct mp* mpsearch1(uint a, int len)
uchar sum(uchar *addr, int len)


lapic.c
void lapicstartap(uchar apicid, uint addr)
void lapiceoi(void)
void lapicw(int index, int value)
int lapicid(void)


ioapic.c
void ioapicenable(int irq, int cpunum)
uint ioapicread(int reg)
void ioapicwrite(int reg, uint data)


console.c
void consoleintr(int (*getc)(void))
int consoleread(struct inode *ip, char *dst, int n)
int consolewrite(struct inode *ip, char *buf, int n)
void panic(char *s)
void cprintf(char *fmt, ...)
void printint(int xx, int base, int sign)
void consputc(int c)
void cgaputc(int c)


uart.c
void uartintr(void)
int uartgetc(void)
void uartputc(int c)


proc.c
struct proc* allocproc(void)
void scheduler(void)
void sched(void)
struct proc* myproc(void)
int cpuid()
struct cpu* mycpu(void)
void wakeup(void *chan)
void wakeup1(void *chan)


swtch.S
swtch


trap.c
void idtinit(void)
void trap(struct trapframe *tf)


syscall.c
int argstr(int n, char **pp)
int argint(int n, int *ip)
int fetchstr(uint addr, char **pp)
int fetchint(uint addr, int *ip)


fs.h
#define IPB
#define IBLOCK(i, sb)
#define BPB
#define BBLOCK(b, sb)


fs.c
struct inode* namei(char *path)
struct inode* nameiparent(char *path, char *name)
struct inode* namex(char *path, int nameiparent, char *name)
char* skipelem(char *path, char *name)
int dirlink(struct inode *dp, char *name, uint inum)
int namecmp(const char *s, const char *t)
void iinit(int dev)
int readi(struct inode *ip, char *dst, uint off, uint n)
int writei(struct inode *ip, char *src, uint off, uint n)
struct inode* ialloc(uint dev, short type)
struct inode* iget(uint dev, uint inum)
struct inode* idup(struct inode *ip)
void ilock(struct inode *ip)
void iunlockput(struct inode *ip)
void iunlock(struct inode *ip)
void iput(struct inode *ip)
void itrunc(struct inode *ip)
void iupdate(struct inode *ip)
uint bmap(struct inode *ip, uint bn)
uint balloc(uint dev)
void bfree(int dev, uint b)
void bzero(int dev, int bno)
void readsb(int dev, struct superblock *sb)


log.c
void initlog(int dev)
void recover_from_log(void)
void begin_op(void)
void end_op(void)
void commit()
void log_write(struct buf *b)
void install_trans(void)
void write_log(void)
void write_head(void)
void read_head(void)


bio.c
struct buf* bread(uint dev, uint blockno)
struct buf* bget(uint dev, uint blockno)
void bwrite(struct buf *b)
void brelse(struct buf *b)


ide.c
void ideintr(void)
int idewait(int checkerr)
void iderw(struct buf *b)
void idestart(struct buf *b)


spinlock.c
void initlock(struct spinlock *lk, char *name)
void acquire(struct spinlock *lk)
int holding(struct spinlock *lock)
void release(struct spinlock *lk)
void pushcli(void)
void popcli(void)
void getcallerpcs(void *v, uint pcs[])


sleeplock.c
void initsleeplock(struct sleeplock *lk, char *name)
void acquiresleep(struct sleeplock *lk)
int holdingsleep(struct sleeplock *lk)
void releasesleep(struct sleeplock *lk)


システムコール(着手中)
1、fork()
2、exit()
3、wait()
4、pipe(p)
5、read(fd, buf, n)
6、kill(pid)
7、exec(filename, *argv)
8、fstat(fd)
9、chdir(dirname)
10、dup(fd)
11、getpid()
12、sbrk(n)
13、sleep(n)
14、uptime
15、open(filename, flags)
16、write(fd, buf, n)
17、mknod(name, major, minor)
18、unlink(filename)
19、link(f1, f2)
20、mkdir(dirname)
21、close(fd)


割り込み処理関連(未着手)
・タイマー割り込み
IDEによる割り込み
・キーボードによる割り込み
・UARTによる割り込み
・サプリアス割り込み
・割り込みによるプロセス切り替え




日本語がおかしい文章、間違った内容を説明している文章、説明がわかりにくい文章、などありましたら、https://twitter.com/jupiteroaknp までコメント下さい。




次回
jupiteroak.hatenablog.com