• 自制操作系统Antz(8)——实现内核 (中) 扩展内核


    Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html

      在前几天的任务中,我们已经简单实现了MBR,直接操作显示器和硬盘操作来加载其他扇区的程序,如今已经可以进入保护模式了,并且编写了我们自己的内核程序,这个内核虽然什么也没有做,但还是成功被加载进内存了。接下来我们要将这个内核程序编写详细的内容了。

    0. 切换堆栈和GDT

     1 SELECTOR_KERNEL_CS    equ    8
     2 
     3 extern    cstart
     4 extern    gdt_ptr
     5 
     6 [SECTION .bss]
     7 StackSpace        resb    2 * 1024
     8 StackTop:        
     9 
    10 [section .text]    
    11 global _start    
    12 
    13 _start:
    14     mov    esp, StackTop
    15     sgdt    [gdt_ptr]    
    16     call    cstart    
    17     lgdt    [gdt_ptr]    
    18     jmp    SELECTOR_KERNEL_CS:csinit
    19 csinit:        
    20     hlt

      这四行代码就可以完成切换堆栈和更换GDT任务了。StackTop定义在.bss段中,大小为2KB,操作GDT时用到了gdt_ptr和cstart分别时一个全局变量和全局函数,定义在c代码start.c中。

    #include "type.h"
    #include "const.h"
    #include "protect.h"
    
    PUBLIC    void*    memcpy(void* pDst, void* pSrc, int iSize);
    PUBLIC void    disp_str(char * pszInfo);
    
    PUBLIC    t_8            gdt_ptr[6];     
    PUBLIC    DESCRIPTOR        gdt[GDT_SIZE];
     
    PUBLIC void cstart()
    {
        disp_str("
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    -----"cstart" begins-----
    ");
    
        memcpy(    &gdt,                    
            (void*)(*((t_32*)(&gdt_ptr[2]))),   
            *((t_16*)(&gdt_ptr[0])) + 1        
            );
    
        t_16* p_gdt_limit = (t_16*)(&gdt_ptr[0]);
        t_32* p_gdt_base  = (t_32*)(&gdt_ptr[2]);
        *p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) - 1;
        *p_gdt_base  = (t_32)&gdt;
    }

      cstart()首先把位于Loader中的原GDT全部复制给新的GDT,然后把gdt_ptr中的内容换为新的GDT的基地址和界限。复制GDT用的是memepy,至于它的函数定义就不详细写了,这个是c中非常出名的一个函数了。

      当然还有一些类型,结构体和宏,这些可以放置在.h的头文件中。

      protect.h :

     1 #ifndef    _TINIX_PROTECT_H_
     2 #define    _TINIX_PROTECT_H_
     3 
     4 typedef struct s_descriptor        /* 共 8 个字节 */
     5 {
     6     t_16    limit_low;         
     7     t_16    base_low; 
     8     t_8    base_mid;     
     9     t_8    attr1;         
    10     t_8    limit_high_attr2; 
    11     t_8    base_high;         
    12 }DESCRIPTOR;
    13 
    14 #endif

      type.h :

     1 #ifndef    _TINIX_TYPE_H_
     2 #define    _TINIX_TYPE_H_
     3 
     4 
     5 typedef    unsigned int        t_32;
     6 typedef    unsigned short        t_16;
     7 typedef    unsigned char        t_8;
     8 typedef    int            t_bool;
     9 
    10 
    11 #endif 

      const.h :

     1 #ifndef    _TINIX_CONST_H_
     2 #define    _TINIX_CONST_H_
     3 
     4 
     5 #define    PUBLIC     
     6 #define    PRIVATE    static    
     7 
     8 #define    GDT_SIZE    128
     9 
    10 
    11 #endif

      接下来在linux下编译链接。

      nasm -f elf -o kernel.o kernel.asm

      nasm -f elf -o string.o string.asm

      gcc -c -o start.o start.c

      ld -s  -Ttext 0x30400 -o kernel.bin kernel.o string.o start.o

      将bin使用工具写入(day01或者dd) ,打开查看结果。

      可以看到cstart成功切换了堆栈与GDT。

    1. Makefile

      随着代码量的增多,编译链接的命令也越来越多了,你可能之前没有接触过Makefile,但这是一个非常高效的东西,值得学习。

      Makefile 是和 make 命令一起配合使用的,很多大型项目的编译都是通过 Makefile 来组织的, 如果没有 Makefile, 那很多项目中各种库和代码之间的依赖关系不知会多复杂,Makefile的组织流程的能力如此之强, 不仅可以用来编译项目, 还可以用来组织我们平时的一些日常操作. 这个需要大家发挥自己的想象力.。

      Makefile基本语法如下:

    1 target ... : prerequisites ...
    2     command
    3     ...
    1 target ... : prerequisites ; command
    2     command
    3     ...

      target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。

      prerequisites就是要生成那个target所需要的文件或是目标。

      command也就是make需要执行的命令。(任意的Shell命令)

      这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。

      来举个例子:

     1 # Makefile for boot
     2 
     3 # Programs, flags, etc.
     4 ASM        = nasm
     5 ASMFLAGS    = 
     6 
     7 # This Program
     8 TARGET        = boot.bin loader.bin
     9 
    10 # All Phony Targets
    11 .PHONY : everything clean all
    12 
    13 # Default starting position
    14 everything : $(TARGET)
    15 
    16 clean :
    17     rm -f $(TARGET)
    18 
    19 all : clean everything
    20 
    21 boot.bin : boot.asm ./include/load.inc ./include/fat12hdr.inc
    22     $(ASM) $(ASMFLAGS) -o $@ $<
    23 
    24 loader.bin : loader.asm ./include/load.inc ./include/fat12hdr.inc ./include/pm.inc
    25     $(ASM) $(ASMFLAGS) -o $@ $<

      #是注释的意思, =用来定义变量 , ASM和ASMFLAGS就是两个变量,使用变量要用$(ASM)和$(ASMFLAGS) 。

      对于 target :  prerequistites 

              command 

      意思就是想要得到target就需要指向命令command。

      target依赖于prerequistites,当prerequistites中至少有一个文件比target文件新时,command才会执行。

      看看最后两行,要想得到loader.bin就需要执行命令:$(ASM) $(ASMFLAGS) -o $@ $<

      loader.bin依赖于loader.asm load.inc fat12hdr.inc pm.inc ,这些中只要有一个比target新,command就会执行。

      那么这里的command是什么意思呢?

    1 $(ASM) $(ASMFLAGS) -o $@ $<

      $@  $< 其实就是target,prerequistites的第一个名字,所以这个命令等价于

    1 nasm -o loader.bin loader.asm

      此外你可能还发现了在外的大标签,他们是动作名称,如everything,all,clean,它们用于make后面,比如make all ,make clean,然后就会执行相应的当作。

      对于Makefile我们目前只需要知道这些就可以了。

      对于Antz内核的编写将会暂时停止几天,最近准备看看Linux内核的相关知识。同步会更新在https://www.cnblogs.com/LexMoon/category/1267413.html

  • 相关阅读:
    1-6注册View Prism官网案例学习
    MVVM复习
    Prism常用类库翻译
    SqlHelper
    ADO.NET复习
    C#复习思维导图
    网络通信基础知识1
    网络通信
    Linux-线程同步之互斥锁
    linux-线程同步之信号量
  • 原文地址:https://www.cnblogs.com/LexMoon/p/antz08.html
Copyright © 2020-2023  润新知