Lab1

清華實(shí)驗(yàn)lab1

本markdown遵循markdown plus與簡(jiǎn)書(shū)與Typora編輯器規(guī)則

若需要使用目錄,請(qǐng)使用markdown plus或Typora

markdwon plus 在線(xiàn)編輯器

[TOC]

練習(xí)1

理解通過(guò) make 生成執(zhí)行文件的過(guò)程。(要求在報(bào)告中寫(xiě)出對(duì)下述問(wèn)題的回答)
在此練習(xí)中,大家需要通過(guò)閱讀代碼來(lái)了解:

  1. 操作系統(tǒng)鏡像文件 ucore.img 是如何一步一步生成的?(需要比較詳細(xì)地解釋 Makefile 中
    每一條相關(guān)命令和命令參數(shù)的含義,以及說(shuō)明命令導(dǎo)致的結(jié)果)
  2. 一個(gè)被系統(tǒng)認(rèn)為是符合規(guī)范的硬盤(pán)主引導(dǎo)扇區(qū)的特征是什么?

練習(xí)1.1

1. 生成ucore.img

查看makefile源碼,在178行處有注釋?zhuān)篶reate ucore.img
以下是此部分代碼

UCOREIMG    := $(call totarget,ucore.img)

$(UCOREIMG): $(kernel) $(bootblock)
    $(V)dd if=/dev/zero of=$@ count=10000
    $(V)dd if=$(bootblock) of=$@ conv=notrunc
    $(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc

$(call create_target,ucore.img)

此處參考dd命令參數(shù) " Linux 下的dd命令使用詳解 "可知道,創(chuàng)建了一塊10000字節(jié)的塊,并且將bootblock復(fù)制過(guò)去。并且把kernel接在之后的位置。(有seek = 1可知 復(fù)制的時(shí)候從文件開(kāi)始跳過(guò)一個(gè)塊,再存放。但此處bootblock為什么只占了一個(gè)塊,會(huì)不會(huì)發(fā)生文件覆寫(xiě) 存疑)。

2. 生成kernel

可以看到上文的依賴(lài)中有kernel,搜索全文得到

# create kernel target
kernel = $(call totarget,kernel)

$(kernel): tools/kernel.ld

$(kernel): $(KOBJS)
    @echo + ld $@
    $(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS)
    @$(OBJDUMP) -S $@ > $(call asmfile,kernel)
    @$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel)

$(call create_target,kernel)

此處涉及到tools/kernel.ld文件,可運(yùn)行查看make指令的實(shí)際樣子

+ ld bin/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

可以得出結(jié)論,此處鏈接的是如上的文件,也就是kern目錄下的全部文件編譯而成的文件。

gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector
此處的全部文件使用類(lèi)如上格式編譯 相關(guān)的編譯參數(shù)有

  • 不適用c語(yǔ)言的內(nèi)建函數(shù),解決函數(shù)名沖突的情況
  • 打開(kāi)警告開(kāi)關(guān)
  • 生成gdb調(diào)試信息
  • 生成32位環(huán)境代碼
  • 生成stabs格式的調(diào)試信息
  • 不使用標(biāo)準(zhǔn)庫(kù),內(nèi)核代碼不需要標(biāo)準(zhǔn)io
  • 禁用堆棧保護(hù),這一條的作用如下 from stackoverflow ,并沒(méi)有讀的很懂
    (In the standard/stock GCC, stack protector is off by default. However, some Linux distributions have patched GCC to turn it on by default. In my opinion, this is rather harmful, as it breaks the ability to compile anything that's not linked against the standard userspace libraries unless the Makefile specifically disables stack protector. It would even break the Linux kernel build except that the distributions with this hack added additional hacks to GCC to detect that the kernel is being built and disable it.)

3.生成bootblock

bootfiles = $(call listf_cc,boot)
$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc))

bootblock = $(call totarget,bootblock)

$(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign)
    @echo + ld $@
    $(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock)
    @$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock)
    @$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock)
    @$(call totarget,sign) $(call outfile,bootblock) $(bootblock)

$(call create_target,bootblock)

代碼如上,一樣通過(guò)運(yùn)行來(lái)查看實(shí)際的運(yùn)行情況

ld -m    elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o

此處有一個(gè)用sign規(guī)范bootblock.o到bin/block.o的操作

'obj/bootblock.out' size: 472 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
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.0237835 s, 215 MB/s

猜想是規(guī)范為 "符合規(guī)范的硬盤(pán)主引導(dǎo)扇區(qū)文件"

以上就是makefile所做的事情,下面加上一張圖以示

makefile編譯流程.jpg


練習(xí)1.2

根據(jù)剛剛的猜想,閱讀sign.c文件

char buf[512];
memset(buf, 0, sizeof(buf));    
···
buf[510] = 0x55;
buf[511] = 0xAA;

這三行代碼表明了 主引導(dǎo)扇區(qū)

  • 大小為512字節(jié)
  • 最后的兩位為AA55(小端機(jī),低位在前)
  • 初始化為全零

練習(xí)2,練習(xí)3

  1. 輸入make debug進(jìn)入調(diào)試界面
  2. 輸入 b *0x7c00設(shè)置斷點(diǎn)
  3. continue運(yùn)行
  4. 查看接下的指令,與boot/bootasm.S和bootblock.asm里的內(nèi)容十分類(lèi)似
start:
.code16                                             # Assemble for 16-bit mode
    cli                                             # Disable interrupts
    cld                                             # String operations increment

    # Set up the important data segment registers (DS, ES, SS).
    xorw %ax, %ax                                   # Segment number zero
    movw %ax, %ds                                   # -> Data Segment
    movw %ax, %es                                   # -> Extra Segment
    movw %ax, %ss                                   # -> Stack Segment

    # Enable A20:
    #  For backwards compatibility with the earliest PCs, physical
    #  address line 20 is tied low, so that addresses higher than
    #  1MB wrap around to zero by default. This code undoes this.
seta20.1:
    inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).
    testb $0x2, %al
    jnz seta20.1

    movb $0xd1, %al                                 # 0xd1 -> port 0x64
    outb %al, $0x64                                 # 0xd1 means: write data to 8042's P2 port

seta20.2:
    inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).
    testb $0x2, %al
    jnz seta20.2

    movb $0xdf, %al                                 # 0xdf -> port 0x60
    outb %al, $0x60                                 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1

    # Switch from real to protected mode, using a bootstrap GDT
    # and segment translation that makes virtual addresses
    # identical to physical addresses, so that the
    # effective memory map does not change during the switch.
    lgdt gdtdesc
    movl %cr0, %eax
    orl $CR0_PE_ON, %eax
    movl %eax, %cr0

    # Jump to next instruction, but in 32-bit code segment.
    # Switches processor into 32-bit mode.
    ljmp $PROT_MODE_CSEG, $protcseg

.code32                                             # Assemble for 32-bit mode
protcseg:
    # Set up the protected-mode data segment registers
    movw $PROT_MODE_DSEG, %ax                       # Our data segment selector
    movw %ax, %ds                                   # -> DS: Data Segment
    movw %ax, %es                                   # -> ES: Extra Segment
    movw %ax, %fs                                   # -> FS
    movw %ax, %gs                                   # -> GS
    movw %ax, %ss                                   # -> SS: Stack Segment

    # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)
    movl $0x0, %ebp
    movl $start, %esp
    call bootmain

    # If bootmain returns (it shouldn't), loop.
spin:
    jmp spin

# Bootstrap GDT
.p2align 2                                          # force 4 byte alignment
gdt:
    SEG_NULLASM                                     # null seg
    SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)           # code seg for bootloader and kernel
    SEG_ASM(STA_W, 0x0, 0xffffffff)                 # data seg for bootloader and kernel

gdtdesc:
    .word 0x17                                      # sizeof(gdt) - 1
    .long gdt                                       # address gdt

代碼后面基本上都帶有注釋?zhuān)诖颂幙偨Y(jié)一下流程

  1. 禁止中斷
  2. 復(fù)位標(biāo)志寄存器方向標(biāo)志位
  3. 初始化ds,es, ss三個(gè)段(設(shè)置為0)
  4. 使能A20(擴(kuò)大尋址空間從1M)
  5. 跳轉(zhuǎn)到gdtdes中,加載GDT(全局描述符表)
  6. 使能cr0,切換到保護(hù)模式
  7. 切換到32位模式
  8. 修改保護(hù)模式下各個(gè)寄存器的值(0x10)
  9. 設(shè)置堆棧以調(diào)用c語(yǔ)言
  10. 調(diào)用bootmain.c

按照練習(xí)3中的要求具體分析切換保護(hù)模式

1. 為何開(kāi)啟A20

A20的歷史可以通過(guò)閱讀wiki/A20_line得知:
大概是8086的時(shí)候使用段加偏移訪(fǎng)問(wèn)的時(shí)候,1M以上的空間必須要第21根線(xiàn)來(lái)尋址,所以才有了A20。
發(fā)展到后來(lái)的80286也沿用8086的方式,80386出來(lái)了保護(hù)模式。此時(shí)地址線(xiàn)已經(jīng)是32根,如果不使能A20的話(huà),A20將保持低電平,訪(fǎng)問(wèn)的空間減少了一半(第21位恒為零)。

2. 如何初始化GDT表

gdt:
    SEG_NULLASM                                     # null seg
    SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)           # code seg for bootloader and kernel
    SEG_ASM(STA_W, 0x0, 0xffffffff)                 # data seg for bootloader and kernel

gdtdesc:
    .word 0x17                                      # sizeof(gdt) - 1
    .long gdt                                       # address gdt

3.如何使能和進(jìn)入保護(hù)模式

使能cr0的wp位為1,進(jìn)入保護(hù)模式。


練習(xí)4

使用source insight可以很方便的查看各種數(shù)據(jù)類(lèi)型和結(jié)構(gòu)體的定義

void
bootmain(void) {
    // read the 1st page off disk
    readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0);

    // is this a valid ELF?
    if (ELFHDR->e_magic != ELF_MAGIC) {
        goto bad;
    }

    struct proghdr *ph, *eph;

    // load each program segment (ignores ph flags)
    ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff);
    eph = ph + ELFHDR->e_phnum;
    for (; ph < eph; ph ++) {
        readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
    }

    // call the entry point from the ELF header
    // note: does not return
    ((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();

bad:
    outw(0x8A00, 0x8A00);
    outw(0x8A00, 0x8E00);

    /* do nothing */
    while (1);
}

先通過(guò)readseg函數(shù)讀取扇區(qū),readseg函數(shù)則是循環(huán)調(diào)用readsect函數(shù)來(lái)讀取每一個(gè)扇區(qū)的內(nèi)容。

static void
readsect(void *dst, uint32_t secno) {
    // wait for disk to be ready
    waitdisk();

    outb(0x1F2, 1);                         // count = 1
    outb(0x1F3, secno & 0xFF);
    outb(0x1F4, (secno >> 8) & 0xFF);
    outb(0x1F5, (secno >> 16) & 0xFF);
    outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);
    outb(0x1F7, 0x20);                      // cmd 0x20 - read sectors

    // wait for disk to be ready
    waitdisk();

    // read a sector
    insl(0x1F0, dst, SECTSIZE / 4);
}

此處代碼具體的讀取方式存疑,猜想是通過(guò)outb:I/O 上寫(xiě)入 8 位數(shù)據(jù) ( 1 字節(jié) )的方法往不同的io端口寫(xiě)入指令來(lái)實(shí)現(xiàn)不同的功能。

扇區(qū)內(nèi)容讀完以后,進(jìn)入判斷函數(shù)判斷是否為ELF格式的文件“ Loading ELF Binaries ” 配合相關(guān)ELF加載方法文檔,將ELF Header的信息讀如ph中。
按照ph里的信息,將ELF文件載入內(nèi)存,然后再通過(guò)過(guò)函數(shù)入口信息,加載內(nèi)核的入口。


練習(xí)5

棧相關(guān)的寄存器兩個(gè),ebp(基址寄存器)和esp(棧指針寄存器),棧的增長(zhǎng)方向是由高到低
eip是程序指令指針,當(dāng)前程序運(yùn)行的指令

此時(shí)ebp是sum函數(shù)棧的基址,然后eip里面是sum函數(shù)中的第一條指令
sum函數(shù)執(zhí)行完之后,sum函數(shù)棧的內(nèi)容全部出棧,
eip=((uint_t)ebp+1),就是sum函數(shù)之后的指令的地址,然后函數(shù)參數(shù)出棧
然后ebp重新變成main函數(shù)的函數(shù)?;?,ebp=((uint_t)ebp)

我們通過(guò)ebp獲得當(dāng)前函數(shù)棧的基址,eip獲得程序當(dāng)前運(yùn)行的位置。
然后打印兩個(gè)值,由指導(dǎo)方案的示意圖與注釋輸出參數(shù)表

函數(shù)調(diào)用棧結(jié)構(gòu)

然后在調(diào)用print_debuginfo輸出其他的信息。
再通過(guò)結(jié)構(gòu)示意圖,將ebp+1的內(nèi)容(返回地址)給eip
eip = *((uint32_t*)ebp + 1);
再吧ebp的內(nèi)容給ebp回到上層調(diào)用函數(shù)繼續(xù)輸出
ebp = *((uint32_t*)ebp);

原代碼沒(méi)有判斷ebp是否為0,輸出了很多為零的ebp信息,在循環(huán)條件處判斷ebp是否為零就可以了

最后一行的信息由 print_debuginfo函數(shù)輸出

void
print_debuginfo(uintptr_t eip) {
    struct eipdebuginfo info;
    if (debuginfo_eip(eip, &info) != 0) {
        cprintf("    <unknow>: -- 0x%08x --\n", eip);
    }
    else {
        char fnname[256];
        int j;
        for (j = 0; j < info.eip_fn_namelen; j ++) {
            fnname[j] = info.eip_fn_name[j];
        }
        fnname[j] = '\0';
        cprintf("    %s:%d: %s+%d\n", info.eip_file, info.eip_line,
                fnname, eip - info.eip_fn_addr);
    }
}

輸出的信息分別是:源碼所在文件,源碼所在行數(shù),函數(shù)名,當(dāng)前位置與函數(shù)指針的差(即函數(shù)源碼的長(zhǎng)度)

至于
最深層ebp

這里的被ebp為什么是7bf8。
其實(shí)很好理解,因?yàn)槊總€(gè)函數(shù)體之前在編譯的時(shí)候都會(huì)被插入

pushl %ebp
movl %esp,%ebp

所以7c00-0008 = 7bf8


練習(xí)6

1.中斷描述符表(也可簡(jiǎn)稱(chēng)為保護(hù)模式下的中斷向量表)中一個(gè)表項(xiàng)占多少字節(jié)?其中哪幾位代表中斷處理代碼的入口?

image.png

由指導(dǎo)方案的上圖可知,一個(gè)表項(xiàng)占32*2位,8個(gè)字節(jié)。0到15位和48到63位為偏移量的低位和高位。16到31位是段選擇子。 通過(guò)這幾個(gè)數(shù)據(jù)來(lái)找到中斷處理代碼的入口。

2.補(bǔ)全idt_init函數(shù)
void idt_init(void)
{
    /* LAB1 YOUR CODE : STEP 2 */
    /* (1) Where are the entry addrs of each Interrupt Service Routine (ISR)?
      *     All ISR's entry addrs are stored in __vectors. where is uintptr_t __vectors[] ?
      *     __vectors[] is in kern/trap/vector.S which is produced by tools/vector.c
      *     (try "make" command in lab1, then you will find vector.S in kern/trap DIR)
      *     You can use  "extern uintptr_t __vectors[];" to define this extern variable which will be used later.
      * (2) Now you should setup the entries of ISR in Interrupt Description Table (IDT).
      *     Can you see idt[256] in this file? Yes, it's IDT! you can use SETGATE macro to setup each item of IDT
      * (3) After setup the contents of IDT, you will let CPU know where is the IDT by using 'lidt' instruction.
      *     You don't know the meaning of this instruction? just google it! and check the libs/x86.h to know more.
      *     Notice: the argument of lidt is idt_pd. try to find it!
      */
    int i = 0;
    extern uintptr_t __vectors[];
    for(i = 0; i < 255; ++i)
    {
        SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], 0);
    }
    SETGATE(idt[T_SWITCH_TOK], 1, GD_KTEXT, __vectors[T_SWITCH_TOK], 3);
    lidt(&idt_pd);
}
  • 根據(jù)上面的注釋?zhuān)壬昝魍獠孔兞縚_vectors[],也就是偏移量。
  • 然后使用SETGATE宏來(lái)設(shè)置idt表。第二個(gè)參數(shù)按照要求設(shè)為零(for 中斷)
    第三個(gè)參數(shù)按照實(shí)驗(yàn)手冊(cè)上說(shuō)的通常設(shè)置為內(nèi)核代碼段,猜想應(yīng)該折折以一個(gè)有關(guān)內(nèi)存管理的宏定義,查看mm文件下的.h文件,發(fā)現(xiàn)在memlayout.h里定義了相關(guān)的宏
/* This file contains the definitions for memory management in our OS. */

/* global segment number */
#define SEG_KTEXT    1
#define SEG_KDATA    2
#define SEG_UTEXT    3
#define SEG_UDATA    4
#define SEG_TSS        5

/* global descriptor numbers */
#define GD_KTEXT    ((SEG_KTEXT) << 3)        // kernel text
#define GD_KDATA    ((SEG_KDATA) << 3)        // kernel data
#define GD_UTEXT    ((SEG_UTEXT) << 3)        // user text
#define GD_UDATA    ((SEG_UDATA) << 3)        // user data
#define GD_TSS        ((SEG_TSS) << 3)        // task segment selector
  • 第四個(gè)參數(shù)設(shè)置為偏移量__vector[i]
  • 第五個(gè)參數(shù)按照指導(dǎo)手冊(cè)上所寫(xiě),除了系統(tǒng)調(diào)用使用特權(quán)級(jí)3以外,均使用特權(quán)級(jí)0
  • 然后是專(zhuān)門(mén)設(shè)置系統(tǒng)調(diào)用中斷,即用戶(hù)態(tài)切換到內(nèi)核態(tài),使用陷阱門(mén)描述符,并且特權(quán)級(jí)為3
  • 最后使用lidt加載中斷描述符表。此處雖然注釋上寫(xiě)參數(shù)是idt_pd,這個(gè)變量類(lèi)型是struct gatedesc 類(lèi)型。但在寫(xiě)的時(shí)候發(fā)現(xiàn)lidt的申明是lidt(struct pseudodesc *pd) 所以將idt_pd取地址傳入。

練習(xí)ex 1

擴(kuò)展proj4,增加syscall功能,即增加一用戶(hù)態(tài)函數(shù)(可執(zhí)行一特定系統(tǒng)調(diào)用:獲得時(shí)鐘計(jì)數(shù)值),當(dāng)內(nèi)核初始完畢后,可從內(nèi)核態(tài)返回到用戶(hù)態(tài)的函數(shù),而用戶(hù)態(tài)的函數(shù)又通過(guò)系統(tǒng)調(diào)用得到內(nèi)核態(tài)的服務(wù)

此處參考《操作系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)》中的代碼

case T_SWITCH_TOU:
        if (tf->tf_cs != USER_CS) {
            switchk2u = *tf;
            switchk2u.tf_cs = USER_CS;
            switchk2u.tf_ds = USER_DS;
            switchk2u.tf_es = USER_DS;
            switchk2u.tf_ss = USER_DS;
            switchk2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;
        
            //設(shè)置EFLAG的i/o特權(quán)位,在用戶(hù)態(tài)也能使用i/o指令
            switchk2u.tf_eflags |= (3<<12);
        
            //設(shè)置臨時(shí)棧,以便cpu用switchk2u中恢復(fù)數(shù)據(jù)
            *((uint32_t *)tf - 1) = (uint32_t)&switchk2u;
        }
        break;  
    case T_SWITCH_TOK:
        if (tf->tf_cs != KERNEL_CS) {
            tf->tf_cs = KERNEL_CS;
            tf->tf_ds = tf->tf_es = KERNEL_DS;

            //關(guān)閉用戶(hù)態(tài)使用io的特權(quán)位
            tf->tf_eflags &= ~(3<<12);
            switchu2k = (struct trapframe *)(tf->tf_esp - (sizeof(struct trapframe) - 8));
            memmove(switchu2k, tf, sizeof(struct trapframe) - 8);
            *((uint32_t *)tf - 1) = (uint32_t)switchu2k;
        }
        break;
棧情況K2U.png

棧情況恢復(fù)寄存器.png

圖片來(lái)源網(wǎng)絡(luò),權(quán)侵刪

根據(jù)操作系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)里的指導(dǎo),先申明臨時(shí)變量
struct trapframe switchk2u, *switchu2k;

然后在trap_disptach函數(shù)中,設(shè)置寄存器為用戶(hù)/內(nèi)核。
最后根據(jù)上面的兩張圖保存棧里的值。

在trap函數(shù)運(yùn)行結(jié)束后,在trapentry.s里的iret的值返回以后,以用戶(hù)態(tài)/內(nèi)核態(tài)繼續(xù)執(zhí)行。

在init.c中啟動(dòng)測(cè)試函數(shù),

static void
lab1_switch_to_user(void) {
    //LAB1 CHALLENGE 1 : TODO
    asm volatile (
        "sub $0x8, %%esp \n"      //從中斷返回的時(shí)候會(huì)多pop兩位用來(lái)更新ss和sp,所以先把棧壓兩位
        "int %0 \n"
        "movl %%ebp, %%esp"    //修復(fù)esp
        : 
        : "i"(T_SWITCH_TOU)
    );
}

static void
lab1_switch_to_kernel(void) {
    //LAB1 CHALLENGE 1 :  TODO
    asm volatile (
        "int %0 \n"
        "movl %%ebp, %%esp \n"
        : 
        : "i"(T_SWITCH_TOK)
    );
}

兩段內(nèi)聯(lián)匯編的作用與含義依然存在疑問(wèn)。


練習(xí)ex 2

(未成功實(shí)現(xiàn)
嘗試在trap.c里重寫(xiě)switch_to_u/k函數(shù),然后通過(guò)鍵盤(pán)中斷調(diào)用系統(tǒng)調(diào)用中斷,使用中斷嵌套的方式實(shí)現(xiàn)

 case IRQ_OFFSET + IRQ_KBD:
        c = cons_getc();
        cprintf("kbd [%03d] %c\n", c, c);
        if('0' == c && tf->tf_cs != KERNEL_CS){
            print_trapframe(tf);

            lab1_switch_to_kernel();
            cprintf("switch to kernel : \n");
            print_trapframe(tf);
            
        }
        else if('3' == c && tf->tf_cs != USER_CS){
            print_trapframe(tf);

            lab1_switch_to_userl();
            cprintf("switch to user : \n");
            print_trapframe(tf);

        }else
            
            break;

實(shí)際運(yùn)行的時(shí)候,首先是無(wú)法觀察到寄存器的變化。理論在調(diào)用switch函數(shù)的時(shí)候,會(huì)有新的中斷產(chǎn)生,會(huì)再次調(diào)用trap函數(shù),在trap函數(shù)返回的時(shí)候,.s文件里的iret會(huì)執(zhí)行,寄存器的值被寫(xiě)入應(yīng)該會(huì)有不同的輸出。

然后就是內(nèi)核態(tài)在調(diào)用鍵盤(pán)中斷時(shí)轉(zhuǎn)入用戶(hù)態(tài)是成功了的,但是之后的用戶(hù)態(tài)轉(zhuǎn)入內(nèi)核態(tài)一直失敗,原因也仍在探索。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容