ucore lab1实验报告

ucore lab1实验报告

实验步骤
练习1: 理解通过make生成执行文件的过程
问题1:
操作系统镜像文件ucore.img是如何一步一步生成的?
实验过程:
先进入文件路径:
home/moocos/ucore_lab/labcodes_answer/lab1_result/

执行make:
make “V=”

观察过程
(1)通过GCC编译器将Kernel目录下的.c文件编译成OBJ目录下的.o文件。

+ cc kern/init/init.c //编译init.c
gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.ok
+ cc kern/libs/readline.c //编译readline.c
gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/readline.c -o obj/kern/libs/readline.o
+ cc kern/libs/stdio.c //编译stdlio.c
gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/stdio.c -o obj/kern/libs/stdio.o
+ cc kern/debug/kdebug.c //编译kdebug.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kdebug.c -o obj/kern/debug/kdebug.o
+ cc kern/debug/kmonitor.c //编译komnitor.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kmonitor.c -o obj/kern/debug/kmonitor.o
+ cc kern/debug/panic.c //编译panic.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/panic.c -o obj/kern/debug/panic.o
+ cc kern/driver/clock.c //编译clock.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/clock.c -o obj/kern/driver/clock.o
+ cc kern/driver/console.c //编译console.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/console.c -o obj/kern/driver/console.o
+ cc kern/driver/intr.c //编译intr.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/intr.c -o obj/kern/driver/intr.o
+ cc kern/driver/picirq.c //编译prcirq.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/picirq.c -o obj/kern/driver/picirq.o
+ cc kern/trap/trap.c //编译trap.c
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trap.c -o obj/kern/trap/trap.o
+ cc kern/trap/trapentry.S //编译trapentry.S
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trapentry.S -o obj/kern/trap/trapentry.o
+ cc kern/trap/vectors.S //编译vectors.S
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/vectors.S -o obj/kern/trap/vectors.o
+ cc kern/mm/pmm.c //编译pmm.c
gcc -Ikern/mm/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/mm/pmm.c -o obj/kern/mm/pmm.o
+ cc libs/printfmt.c //编译printfmt.c
gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/printfmt.c -o obj/libs/printfmt.o
+ cc libs/string.c //编译string.c
gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/string.c -o obj/libs/string.o

(2)ld命令根据链接脚本文件kernel.ld将生成的*.o文件,链接成BIN目录下的kernel文件。

+ ld bin/kernel //链接成kernel
ld -m    elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel  obj/kern/init/init.o obj/kern/libs/readline.o obj/kern/libs/stdio.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/debug/panic.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/intr.o obj/kern/driver/picirq.o obj/kern/trap/trap.o obj/kern/trap/trapentry.o obj/kern/trap/vectors.o obj/kern/mm/pmm.o  obj/libs/printfmt.o obj/libs/string.o

(3)通过GCC编译器将boot目录下的.c,.S文件以及tools目录下的sign.c文件编译成OBJ目录下的*.o文件。

+ cc boot/bootasm.S //编译bootasm.S
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
+ cc boot/bootmain.c //编译bootmain.c
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
+ cc tools/sign.c //编译sign.c
gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign

(4)ld命令将生成的*.o文件,链接成BIN目录下的bootblock文件。

+ ld bin/bootblock  //根据sign规范生成bootblock
ld -m    elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
'obj/bootblock.out' size: 488 bytes
build 512 bytes boot sector: 'bin/bootblock' success!

(5)dd命令将dev/zero, bin/bootblock,bin/kernel 写入到bin/ucore.img

//创建大小为10000个块的ucore.img,初始化为0,每个块为512字节
dd if=/dev/zero of=bin/ucore.img count=10000
10000+0 records in 
10000+0 records out 
5120000 bytes (5.1 MB) copied, 0.0242234 s, 211 MB/s
//把bootblock中的内容写到第一个块
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.0001916 s, 2.7 MB/s
//从第二个块开始写kernel中的内容
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
146+1 records in
146+1 records out
74923 bytes (75 kB) copied, 0.000380543 s, 197 MB/s

一个被系统认为是符合规范的硬盘主引导扇区的特征有以下几点:
1、磁盘主引导扇区只有512字节
2、磁盘最后两个字节为0x55AA
3、由不超过466字节的启动代码和不超过64字节的硬盘分区表加上两个字节的结束符组成

练习二:使用qemu执行并调试lab1中的软件
内容:
1、从 CPU加电后执行的第一条指令开始,单步跟踪 BIOS的执行
2、在初始化位置 0x7c00 设置实地址断点,测试断点正常
3、从 0x7c00 开始跟踪代码运行,将单步跟踪反汇编得到的代码与 bootasm.S和 bootblock.asm进行比较
4、自己找一个 bootloader或内核中的代码位置,设置断点并进行测试

首先通过make qemu指令运行出等待调试的qemu虚拟机,然后再打开一个终端,通过下述命令连接到qemu虚拟机:
首先通过make lab1-mon指令运行出等待调试的qemu虚拟机,并同时打开一个gdb,执行代码为:

+ cc kern/init/init.c
+ cc kern/libs/readline.c
+ cc kern/libs/stdio.c
+ cc kern/debug/kdebug.c
+ cc kern/debug/kmonitor.c
+ cc kern/debug/panic.c
+ cc kern/driver/clock.c
+ cc kern/driver/console.c
+ cc kern/driver/intr.c
+ cc kern/driver/picirq.c
+ cc kern/trap/trap.c
+ cc kern/trap/trapentry.S
+ cc kern/trap/vectors.S
+ cc kern/mm/pmm.c
+ cc libs/printfmt.c
+ cc libs/string.c
+ ld bin/kernel
+ cc boot/bootasm.S
+ cc boot/bootmain.c
+ cc tools/sign.c
+ ld bin/bootblock
'obj/bootblock.out' size: 488 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB) copied, 0.0586492 s, 87.3 MB/s
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000103106 s, 5.0 MB/s
146+1 records in
146+1 records out
74923 bytes (75 kB) copied, 0.0004648 s, 161 MB/s

可以看到qemu的运行状态:

以及gdb中运行停在了0x7c00处:

0x0000fff0 in ?? ()
warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration
of GDB.  Attempting to continue with the default i8086 settings.

The target architecture is assumed to be i8086
Breakpoint 1 at 0x7c00

Breakpoint 1, 0x00007c00 in ?? ()
=> 0x7c00:	cli    
   0x7c01:	cld    
(gdb) 

在gdb中显示当前的十条指令,并让qemu继续运行,命令是:

x /10i $pc
continue

可以看到qemu己经开始运行了,并且已经到了ucore。
之后待qemu运行完毕,退出:

quit

对比此时bootasm.S中的起始代码,发现确实是一样的。

练习3:分析bootloader进入保护模式的过程
1、关中断和清除数据段寄存器

.globl start
start:
.code16                                             # 使用16位模式编译
    cli                                             # 禁用中断
    cld                                             # 清除方向标志
    # 建立重要的数据段寄存器(DS,ES,SS)。
    xorw %ax, %ax                                   # ax清0
    movw %ax, %ds                                   # ds清0
    movw %ax, %es                                   # es清0
    movw %ax, %ss                                   # ss清0

2、为何开启A20,以及如何开启A20

seta20.1:           # 等待8042输入缓冲区空
    inb $0x64, %al                                  # 从0x64端口中读入一个
    字节到al中
    testb $0x2, %al
             # 测试al的第2位
    jnz seta20.1 
             # al的第2位为0,则跳出循环
             movb $0xd1, %al                                 # 将0xd1写入al中
             outb %al, $0x64
             seta20.2:											# 等待8042输入缓冲区空
  inb $0x64, %al                                  # 从0x64端口中读入一个字节到al中
      testb $0x2, %al         # 测试al的第2位
       jnz seta20.2         # al的第2位为0,则跳出循环 
       movb $0xdf, %al                                 # 将0xdf入al中
    outb %al, $0x60                                 # 将0xdf入到0x64端口中,打开A20       

3、如何初始化GDT表
(1)载入GDT表

 lgdt gdtdesc          # 载入GDT表

(2)进入保护模式,cro的第0位为1表示处于保护模式。

movl %cr0, %eax         # 加载cro到eax
orl $CR0_PE_ON, %eax       # 将eax的第0位置为1
movl %eax, %cr0         # 将cr0的第0位置为1

(3)通过长跳转更新cs的基地址,设置段寄存器,并建立堆栈

ljmp $PROT_MODE_CSEG, $protcseg      # $PROT_MODE_CSEG的值为0x80
.code32                                             # 使用32位模式编译
protcseg:
    movw $PROT_MODE_DSEG, %ax                       # ax赋0x80
    movw %ax, %ds                                   # ds赋0x80
    movw %ax, %es                                   # es赋0x80
    movw %ax, %fs                                   # fs赋0x80
    movw %ax, %gs                                   # gs赋0x80
    movw %ax, %ss                                   # ss赋0x80
    movl $0x0, %ebp         # 设置帧指针
    movl $start, %esp        # 设置栈指针

转到保护模式完成,进入boot主方法。

call bootmain

练习4:分析bootloader加载ELF格式的OS的过程
bootloader如何读取硬盘扇区
1、等待磁盘准备好;
2、发出读取扇区的命令;
3、等待磁盘准备好;
4、把磁盘扇区数据读到指定内存。

bootmain.c:

#include <defs.h>
#include <x86.h>
#include <elf.h>
/* *********************************************************************
 * This a dirt simple boot loader, whose sole job is to boot
 * an ELF kernel image from the first IDE hard disk.
 *
 * DISK LAYOUT
 *  * This program(bootasm.S and bootmain.c) is the bootloader.
 *    It should be stored in the first sector of the disk.
 *
 *  * The 2nd sector onward holds the kernel image.
 *
 *  * The kernel image must be in ELF format.
 *
 * BOOT UP STEPS
 *  * when the CPU boots it loads the BIOS into memory and executes it
 *
 *  * the BIOS intializes devices, sets of the interrupt routines, and
 *    reads the first sector of the boot device(e.g., hard-drive)
 *    into memory and jumps to it.
 *
 *  * Assuming this boot loader is stored in the first sector of the
 *    hard-drive, this code takes over...
 *
 *  * control starts in bootasm.S -- which sets up protected mode,
 *    and a stack so C code then run, then calls bootmain()
 *
 *  * bootmain() in this file takes over, reads in the kernel and jumps to it.
 * */

/* waitdisk - wait for disk ready */
static void
waitdisk(void) { //如果0x1F7的最高2位是01,跳出循环
    while ((inb(0x1F7) & 0xC0) != 0x40)
        /* do nothing */;
}
/* 读节 - 将“secno”处的单个扇区读入“dst” */
static void
readsect(void *dst, uint32_t secno) {
    // 等待磁盘准备就绪
    waitdisk();
    // 用LBA模式的PIO(Program IO)方式来访问硬盘
    outb(0x1F2, 1); //读取一个扇区
    outb(0x1F3, secno & 0xFF); //要读取的扇区编号
    outb(0x1F4, (secno >> 8) & 0xFF); //用来存放读写柱面的低8位字节 
    outb(0x1F5, (secno >> 16) & 0xFF); //用来存放读写柱面的高2位字节
    outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0); // 用来存放要读/写的磁盘号及磁头号
    outb(0x1F7, 0x20);                      // cmd 0x20-读为扇区
    // 等待磁盘准备就绪
    waitdisk();
    // 读一个扇区
    insl(0x1F0, dst, SECTSIZE / 4); //获取数据
}

/* *
 * readseg - read @count bytes at @offset from kernel into virtual address @va,might copy more than asked.
 * */
static void
readseg(uintptr_t va, uint32_t count, uint32_t offset) {
    uintptr_t end_va = va + count;
    // 转至分区边界
    va -= offset % SECTSIZE;
    // 从字节转换到扇区;内核从扇区1开始
    uint32_t secno = (offset / SECTSIZE) + 1; //加1因为0扇区被引导占用
    // 如果速度太慢,我们一次就能读到很多扇区。
    // 我们会写更多的记忆,而不是要求,但这并不重要--我们以增序加载。
    for (; va < end_va; va += SECTSIZE, secno ++) {
        readsect((void *)va, secno);
    }
}

bootloader如何加载ELF格式的OS
1、从硬盘读了8个扇区数据到内存0x10000处,并把这里强制转换成elfhdr使用。
2、校验e_magic字段。
3、根据偏移量分别把程序段的数据读取到内存中。
ElF结构定义

struct elfhdr {
    uint32_t e_magic;     // 判断读出来的ELF格式的文件是否为正确的格式
    uint8_t e_elf[12];
    uint16_t e_type;      // 1=可重定位,2=可执行,3=共享对象,4=核心映像
    uint16_t e_machine;   // 3=x86,4=68K等.
    uint32_t e_version;   // 文件版本,总是1
    uint32_t e_entry;     // 程序入口所对应的虚拟地址。
    uint32_t e_phoff;     // 程序头表的位置偏移
    uint32_t e_shoff;     // 区段标题或0的文件位置
    uint32_t e_flags;     // 特定于体系结构的标志,通常为0
    uint16_t e_ehsize;    // 这个elf头的大小
    uint16_t e_phentsize; // 程序头中条目的大小
    uint16_t e_phnum;     // 程序头表中的入口数目
    uint16_t e_shentsize; // 节标题中条目的大小
    uint16_t e_shnum;     // 节标题中的条目数或0
    uint16_t e_shstrndx;  // 包含节名称字符串的节号。
};

bootmain.c:

#define SECTSIZE        512
#define ELFHDR          ((struct elfhdr *)0x10000) 
/* 引导-引导加载器的条目 */
void
bootmain(void) {
    // 从磁盘上读取第一页
    readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0);
    // 这是有效的ELF吗?
    if (ELFHDR->e_magic != ELF_MAGIC) { // 通过储存在头部的幻数判断是否是合法的ELF文件
        goto bad;
    }
    struct proghdr *ph, *eph;
    // 加载每个程序段(忽略ph标志)
    ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff); // 先将描述表的头地址存在ph
    eph = ph + ELFHDR->e_phnum;
    // 按照描述表将ELF文件中数据载入内存
    for (; ph < eph; ph ++) {
        readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
    }// ELF文件0x1000位置后面的0xd1ec比特被载入内存0x00100000,ELF文件0xf000位置后面的0x1d20比特被载入内存0x0010e000
    // 从ELF报头调用入口点
    // 注:不返回
    ((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();
bad:
    outw(0x8A00, 0x8A00);
    outw(0x8A00, 0x8E00);
    /* do nothing */
    while (1);
}

练习5:实现函数调用堆栈跟踪函数(需要编程)
可以通过read_ebp()和read_eip()函数来获取当前ebp寄存器和eip 寄存器的信息。
然后通过ebp+12,ebp+16,ebp+20,ebp+24来输出4个参数的值,最后更新ebp:ebp=ebp[0],更新eip:eip=ebp[1]。直到ebp 对应地址的值为0(表示当前函数为bootmain)。

read_eip() 函数定义在kdebug.c中:

read_eip(void) {
    uint32_t eip;
    asm volatile("movl 4(%%ebp), %0" : "=r" (eip)); //内联汇编,读取(ebp-4)的值到变量eip
    return eip;
}

read_ebp() 函数定义在x86.h中:

static inline uint32_t
read_ebp(void) {
    uint32_t ebp;
    asm volatile ("movl %%ebp, %0" : "=r" (ebp)); //内联汇编,读取edp寄存器的值到变量ebp
    return ebp;
}

实现函数如下:

print_stackframe(void) {
	int i,j;
	uint32_t ebp=read_ebp();
	uint32_t eip=read_eip();
    for(i=0;i<STACKFRAME_DEPTH&&ebp!=0;i++){
    	cprintf("ebp:0x%08x eip:0x%08x\n",ebp,eip); 
        uint32_t *args=(uint32_t *)ebp+2;
		cprintf("参数:");
        for(j=0;j<4;j++){
            cprintf("0x%08x ", args[j]);
        }
        cprintf("\n");
        print_debuginfo(eip-1);
        eip=((uint32_t *)ebp)[1];
        ebp=((uint32_t *)ebp)[0];
	}
}

执行make qemu:

Special kernel symbols:
  entry  0x00100000 (phys)
  etext  0x001032cf (phys)
  edata  0x0010ea16 (phys)
  end    0x0010fd20 (phys)
Kernel executable memory footprint: 64KB
ebp:0x00007b08 eip:0x001009a6
参数:0x00010094 0x00000000 0x00007b38 0x00100092 
    kern/debug/kdebug.c:307: print_stackframe+21
ebp:0x00007b18 eip:0x00100ca1
参数:0x00000000 0x00000000 0x00000000 0x00007b88 
    kern/debug/kmonitor.c:125: mon_backtrace+10
ebp:0x00007b38 eip:0x00100092
参数:0x00000000 0x00007b60 0xffff0000 0x00007b64 
    kern/init/init.c:48: grade_backtrace2+33
ebp:0x00007b58 eip:0x001000bb
参数:0x00000000 0xffff0000 0x00007b84 0x00000029 
    kern/init/init.c:53: grade_backtrace1+38
ebp:0x00007b78 eip:0x001000d9
参数:0x00000000 0x00100000 0xffff0000 0x0000001d 
    kern/init/init.c:58: grade_backtrace0+23
ebp:0x00007b98 eip:0x001000fe
参数:0x001032fc 0x001032e0 0x0000130a 0x00000000 
    kern/init/init.c:63: grade_backtrace+34
ebp:0x00007bc8 eip:0x00100055
参数:0x00000000 0x00000000 0x00000000 0x00010094 
    kern/init/init.c:28: kern_init+84
ebp:0x00007bf8 eip:0x00007d68
参数:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8 
    <unknow>: -- 0x00007d67 --
++ setup timer interrupts
(THU.CST) os is loading ...

其对应的是第一个使用堆栈的函数,bootmain.c中的bootmain。(因为此时ebp对应地址的值为0)
bootloader设置的堆栈从0x7c00开始,使用”call bootmain”转入bootmain函数。
call指令压栈,所以bootmain中ebp为0x7bf8。

练习6:完善中断初始化和处理(需要编程)
中断描述符表(也可简称为保护模式下的中断向量表)中一个表项占多少字节?其中哪几位代表中断处理代码的入口?

向量表结构:

/* Gate descriptors for interrupts and traps */
struct gatedesc {
    unsigned gd_off_15_0 : 16;        // low 16 bits of offset in segment
    unsigned gd_ss : 16;            // segment selector
    unsigned gd_args : 5;            // # args, 0 for interrupt/trap gates
    unsigned gd_rsv1 : 3;            // reserved(should be zero I guess)
    unsigned gd_type : 4;            // type(STS_{TG,IG32,TG32})
    unsigned gd_s : 1;                // must be 0 (system)
    unsigned gd_dpl : 2;            // descriptor(meaning new) privilege level
    unsigned gd_p : 1;                // Present
    unsigned gd_off_31_16 : 16;        // high bits of offset in segment
};

由各项相加可知,一个表项的占64bit,即8字节,其中0 ~ 15位和48 ~ 63位分别为偏移量的低16位和高16位,两者拼接为偏移量,16~31位为段选择器。
由gd_ss与gd_off构成中断处理代码入口。

2、请编程完善kern/trap/trap.c中对中断向量表进行初始化的函数idt_init。在idt_init函数中,依次对所有中断入口进行初始化。使用mmu.h中的SETGATE宏,填充idt数组内容。每个中断的入口由tools/vectors.c生成,使用trap.c中声明的vectors数组即可。

extern uintptr_t __vectors[];
int i;
for(i=0;i<sizeof(idt)/sizeof(struct gatedesc);i++){
        SETGATE(idt[i],0,GD_KTEXT,__vectors[i],DPL_KERNEL);
}
lidt(&idt_pd);
#define SETGATE(gate, istrap, sel, off, dpl)

使用SETGATE设置每一个中段描述符表项。
3、请编程完善trap.c中的中断处理函数trap,在对时钟中断进行处理的部分填写trap函数中处理时钟中断的部分,使操作系统每遇到100次时钟中断后,调用print_ticks子程序,向屏幕上打印一行文字”100 ticks”。

在kern/driver/clock.h中声明了ticks为extern。

ticks++;
if((ticks%TICK_NUM)==0){
        print_ticks();
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值