Skip to content

clee01/multi_thread_kernel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

83 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

multi_thread_kernel(多线程系统内核)

学习路线

准备

  • 操作系统发行版本
cat /etc/issue

Ubuntu 18.04.1 LTS \n \l
  • 安装nasm汇编语言编译程序
# 安装
sudo apt install nasm

# 编译命令如下
nasm -f <format> <filename> [-o <output>]
  • 安装qemu-system-x86虚拟机软件
# 安装
sudo apt install qemu-system-x86

# 常见使用命令
qemu-system-x86_64 -hda <filename>  # 为客户机指定块存储设备

Hello World Bootloader

# 编译
nasm -f bin ./boot_hello_world.asm -o ./boot_hello_world.bin
# 反汇编(可选的)
ndisasm ./boot_hello_world.bin
# 运行
qemu-system-x86_64 -hda ./boot_hello_world.bin

image

分段内存模型

  • 8086段寄存器

    • CS - Code Segment
    • SS - Stack Segment
    • DS - Data Segment
    • ES - Extra Segment
  • 计算物理地址

    • 物理地址 = 段寄存器 * 16 + 偏移地址
# 编译
nasm -f bin ./boot_segment_memory.asm -o ./boot_segment_memory.bin
# 反汇编(可选的)
ndisasm ./boot_segment_memory.bin
# 运行
qemu-system-x86_64 -hda ./boot_segment_memory.bin

image

BPB应用

image

  • MBR:主引导记录,446字节
  • 主分区(只有四个,16bytes * 4)
    • 活动分区,用来启动操作系统
    • 扩展分区,可以建立逻辑分区
  • 魔数(2bytes)
    • 0x55AA,如果这里被破坏,系统就废了

BIOS+MBR启动过程:

  • 在打开电源时,计算机开始自检过程,从BIOS中载入必要的指令,然后进行一系列的自检操作(进行硬件的初始化检查,包括内存、硬盘、键盘等),同时在屏幕上显示信息。自检完成后,根据CMOS的设置,BIOS加载启动盘,将主引导记录(MBRMaster Boot Record)中的引导代码载入内存,接着,启动过程由MBR来执行。启动代码搜索MBR中的分区表(DPT),找出活动分区,将活动分区的第一个扇区(VBRVolumn Boot Record,卷引导记录,活动分区的VBR也叫DBR)中的引导代码载入内存0x07C00
  • MBR:计算机启动后从可启动介质上首先装入内存并且执行的代码
  • DBRDos Boot Record通常包括一个引导程序和一个被称为BPBBIOS Parameter Block)的本分区参数记录表。引导程序的主要任务是当MBR将系统控制权交给它时,判断本分区根目录前两个文件是不是操作系统的引导文件。如果确定存在,就把它读入内存并把控制权交给该文件。BPB参数块记录着本分区的起始扇区、结束扇区、文件存储格式、硬件介质描述符、根目录大小、FAT个数和分配单元的大小等重要参数
  • VBR:每个非扩展分区以及逻辑分区的第一个扇区(VBR包括DBR(非扩展分区)和EBR),可存放小段程序,用于启动该分区上某程序或者操作系统(DBR
  • 扩展分区中的每个逻辑驱动器都存在一个类似于MBR的扩展引导记录(EBRExtended Boot Record
# 编译
nasm -f bin ./boot_real_hardware.asm -o ./boot_real_hardware.bin
# 反汇编(可选的)
ndisasm ./boot_real_hardware.bin
# 列出所有可用的分区类型
sudo fdisk -l
# 读取、转换并输出数据 !注意,确保sdb(这里是USB)上没有重要内容
sudo dd if ./boot_real_hardware.bin of=/dev/sdb
# 后续可以用这个USB作为bootloader启动计算机

中断向量表

# 编译
nasm -f bin ./boot_interrupt_vector_table.asm -o ./boot_interrupt_vector_table.bin
# 反汇编(可选的)
ndisasm ./boot_interrupt_vector_table.bin
# 运行
qemu-system-x86_64 -hda ./boot_interrupt_vector_table.bin

image

读取硬盘

DISK - WRITE DISK SECTOR(S)

# 编译
nasm -f bin ./boot_read_hard_disk.asm -o ./boot_read_hard_disk.bin
# 查看message.txt
cat message.txt

bingo!

# 追加message.txt到boot_read_hard_disk.bin
dd if=./message.txt >> ./boot_read_hard_disk.bin
# 追加sector到boot_read_hard_disk.bin
dd if=/dev/zero bs=512 count=1 >> ./boot_read_hard_disk.bin
# 安装十六进制编辑器
sudo apt install bless
# 查看bin文件
bless boot_read_hard_disk.bin

image

# 运行
qemu-system-x86_64 -hda ./boot_read_hard_disk.bin

image

保护模式

# 编译
nasm -f bin ./boot_protected_mode.asm -o ./boot_protected_mode.bin
# 安装gdb
sudo apt install gdb
# 运行gdb
gdb
# 连接调试
(gdb) target remote | qemu-system-x86_64 -hda ./boot_protected_mode.bin -S -gdb stdio

image

# 继续,可以看到程序运行就停在了"Booting from Hard Disk..."下一行处
(gdb) c
Continuing.

image

# ctrl-c中断,可以看到程序是运行在0x7c7f处被中断的
^C

image

# 显示汇编窗口,可以看到0x7c7f处是"jmp $"指令,符合预期
(gdb) layout asm

image

# 可以看到对应各个寄存器设定的值也是和程序中所设定的一致
info registers

image

开启A20

A20或地址线20是构成基于x86的计算机系统总线的电线之一。A20线特别用于传输地址总线上的第21位。

in al, 0x92
or al, 2
out 0x92, al

加载32位内核程序至内存

# 脚本编译
./build.sh
# 运行gdb
gdb
# 载入symbol file
(gdb) add-symbol-file build/kernelfull.o 0x100000
# 打断点
(gdb) break _start
# 连接调试
(gdb) target remote | qemu-system-x86_64 -hda ./bin/os.bin -S -gdb stdio
# 开始执行(可以看到停在了断点处)
(gdb) c
Continuing.

Breakpoint 1, 0x0000000000100000 in _start ()
# 显示汇编窗口
(gdb) layout asm

image

# 单步调试
(gdb) stepi

image

运行C代码

...
# 打断点
(gdb) break kernel_main

image

# 查看当前堆栈
(gdb) bt

image

# 单步调试
(gdb) stepi

image

屏幕显示Hello world

实现中断描述符表

实现I/O读取接口

// src/io/io.h
// 函数原型
unsigned char insb(unsigned short port);
unsigned short insw(unsigned short port);

void outb(unsigned short port, unsigned char val);
void outw(unsigned short port, unsigned short val);

可编程中断处理器(PIC)实现

setup_pic:
    ; Initialize some flags in the PIC's
    mov al, 00010001b  ; b4=1: Init; b3=0: Edge; b1=0: Cascade; b0=1: Need 4th init step
    out 0x20, al  ; Tell master

    mov al, 0x20  ; Master IRQ0 should be on INT 0x20 (Just after intel exception)
    out 0x21, al

    mov al, 00000001b  ; b4=0: FNM; b3-2=00: Master/Slave set by hardware; b1=0: Not AEOI; b0=1: x86 mode
    out 0x21, al

    ret

自定义堆实现

void* ptr = kmalloc(50);  // 0x1000000
void* ptr2 = kmalloc(5000);  // 0x1001000
void* ptr3 = kmalloc(5600);  // 0x1003000
kfree(ptr);
void* ptr4 = kmalloc(50);  // 0x1000000

注意:此时需要用qemu-system-i386执行,模拟32Intel架构CPU

创建中断使能函数

void enable_interrupts();
void disable_interrupts();

删除kernel.asm中的sti语句,可能有潜在的panic风险

分页机制

; 开启分页
[BITS 32]

section .asm

global paging_load_directory
global enable_paging

paging_load_directory:
    push ebp
    mov ebp, esp
    mov eax, [ebp+8]
    mov cr3, eax
    pop ebp
    ret

enable_paging:
    push ebp
    mov ebp, esp
    mov eax, cr0
    or eax, 0x80000000
    mov cr0, eax
    pop ebp
    ret

调整页表

磁盘读取(C

outb(0x1F6, (lba >> 24) | 0xE0);
outb(0x1F2, total);
outb(0x1F3, (unsigned char)(lba & 0xFF));
outb(0x1F4, (unsigned char)(lba >> 8));
outb(0x1F5, (unsigned char)(lba >> 16));
outb(0x1F7, 0x20);

unsigned short* ptr = (unsigned short*)buf;
for (int b = 0; b < total; ++b) {
    // Wait for the buffer to be ready
    char c = insb(0x1F7);
    while (!(c & 0x08)) {
        c = insb(0x1F7);
    }

    // Copy from hard disk to memory
    for (int i = 0; i < 256; ++i) {
        *ptr = insw(0x1F0);
        ptr++;
    }
}

改进磁盘驱动程序

// in /src/disk/disk.h
void disk_search_and_init();
struct disk* disk_get(int index);
int disk_read_block(struct disk* idisk, unsigned int lba, int total, void* buf);

创建路径解析器

// in src/fs/pparser.h
void pathparser_free(struct path_root* root);
struct path_root* pathparser_parse(const char* path, const char* current_directory_path);

创建一个磁盘流

// in src/disk/streamer.h
struct disk_stream* diskstream_new(int disk_id);
int diskstream_seek(struct disk_stream* stream, int pos);
int diskstream_read(struct disk_stream* stream, void* out, int total);
void diskstream_close(struct disk_stream* stream);

创建FAT文件系统

; FAT16 Header
OEMIdentifier           db 'PEACHOS '
BytesPerSector          dw 0x200
SectorsPerCluster       db 0x80
ReservedSectors         dw 200
FATCopies               db 0x02
RootDirEntries          dw 0x40
NumSectors              dw 0x00
MediaType               db 0xF8
SectorsPerFat           dw 0x100
SectorsPerTrack         dw 0x20
NumberOfHeads           dw 0x40
HiddenSectors           dd 0x00
SectorsBig              dd 0x773594

; Extended BPB (Dos 4.0)
DriveNumber             db 0x80
WinNTBit                db 0x00
Signature               db 0x29
VolumeID                dd 0xD105
VolumeIDString          db 'PEACHOS BOO'
SystemIDString          db 'FAT16   '

实现VFS核心功能

实现FAT16文件系统驱动核心功能

实现FAT16各类结构

实现FAT16解析函数

实现FAT16 fopen函数

// in src/fs/file.c
int fopen(const char* filename, const char* mode_str);

// in src/fs/fat/fat16.c
void* fat16_open(struct disk* disk, struct path_part* path, FILE_MODE mode);

实现FAT16 fread函数

// in src/fs/file.c
int fread(void* ptr, uint32_t size, uint32_t nmemb, int fd);

// in src/fs/fat/fat16.c
int fat16_read(struct disk* disk, void* descriptor, uint32_t size, uint32_t nmemb, char* out_ptr);

实现FAT16 fseek函数

// in src/fs/file.c
int fseek(int fd, int offset, FILE_SEEK_MODE whence);

// in src/fs/fat/fat16.c
int fat16_seek(void *private, uint32_t offset, FILE_SEEK_MODE seek_mode);

实现FAT16 fstat函数

// in src/fs/file.c
int fstat(int fd, struct file_stat* stat);

// in src/fs/fat/fat16.c
int fat16_stat(struct disk* disk, void* private, struct file_stat* stat);

实现FAT16 fclose函数

// in src/fs/file.c
int fclose(int fd);

// in src/fs/fat/fat16.c
int fat16_close(void* private);

实现panic函数

// in src/kernel.h
void panic(const char* msg);

用C实现全局描述表

实现TSS(Task Switch Segment)

实现Task相关函数

struct task* task_new();
struct task* task_current();
struct task* task_get_next();
int task_free(struct task* task);

实现进程相关函数

// in src/task/process.c
int process_load(const char* filename, struct process** process);

实现用户态功能

创建第一个用户程序

; in programs/blank/blank.asm
_start:

label:
    jmp label
# 反汇编 用户程序
ndisasm -b32 programs/blank/blank.bin
00000000  EBFE              jmp short 0x0

运行用户程序

  • 可以看到,用户程序一直循环执行在此处jmp 0x400000

image

创建第一个内核命令

从用户进程的任务中读取字符串

读取用户进程任务的栈空间

  • 用户任务中断调用内核例程时,可以将入参push到当前进程的栈空间,内核例程运行时,可以通过函数task_get_stack_item获取

创建内核输出字符命令

创建抽象键盘层

创建PS2端口键盘驱动

优化中断描述表设计

进程切换

用户态获取键盘输入

终端逐个输出字符

终端实现退格键

实现ELF Loader

About

多線程系統內核

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published