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

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

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

  Linux内核源码分析地址: https://www.cnblogs.com/LexMoon/category/1267413.html

  Github地址:https://github.com/CasterWx 

  在前几天的任务中,我们已经简单实现了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("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n-----\"cstart\" begins-----\n");

    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

 

目录
相关文章
|
10天前
|
安全 程序员 Linux
探索操作系统的心脏:内核与用户空间的交互之旅
【9月更文挑战第9天】本文是一次深入操作系统核心的探险,我们不仅会穿越神秘的内核世界和多彩的用户空间,还将揭秘它们之间如何通过系统调用、库函数、API等桥梁进行信息交换。这不仅是一段技术之旅,也是对操作系统设计哲学的思考。我们将用浅显的语言,逐步揭开操作系统的神秘面纱,让读者在轻松愉快的阅读中,获得对操作系统深层次理解的钥匙。
|
2天前
|
算法 调度 Python
探索操作系统的内核——一个简单的进程调度示例
【9月更文挑战第17天】在这篇文章中,我们将深入探讨操作系统的核心组件之一——进程调度。通过一个简化版的代码示例,我们将了解进程调度的基本概念、目的和实现方式。无论你是初学者还是有一定基础的学习者,这篇文章都将帮助你更好地理解操作系统中进程调度的原理和实践。
|
1天前
|
存储 安全 程序员
探索操作系统的心脏:内核设计与实现
【9月更文挑战第18天】在数字世界的大厦中,操作系统犹如其坚实的基石。本文旨在揭开操作系统核心——内核的神秘面纱,通过浅显易懂的语言和生动的比喻,带领读者一探内核的设计奥秘与实现细节。我们将一同穿梭于进程管理、内存分配、文件系统等关键领域,领略内核如何协调资源、保障安全,并确保系统的高效运行。你将发现,即便是隐藏在幕后的代码,也有着它的节奏与旋律,它们共同奏响了数字世界中最动听的交响曲。
|
1天前
|
安全 API 调度
探索操作系统的心脏:内核与用户空间的交互
【9月更文挑战第18天】在数字世界的海洋中,操作系统扮演着至关重要的角色。它不仅是软件与硬件之间的桥梁,更是维护整个计算生态平衡的守护者。本文将深入探讨操作系统的核心组件——内核,以及它如何与用户空间进行交互,确保系统的稳定运行和资源的合理分配。通过简化的比喻和生动的实例,我们将一探究竟,看看这个不为人知的幕后英雄是如何默默支撑起我们的数字生活的。
|
9天前
|
物联网 调度 云计算
探索操作系统的心脏:内核与用户空间的交互之旅
【9月更文挑战第10天】在数字世界的海洋中,操作系统扮演着船舶的角色,而它的内核则是这艘船的发动机。本文将揭开操作系统内核的神秘面纱,通过浅显的语言和实际代码示例,带领读者理解内核与用户空间之间的交互机制。我们将从基础概念出发,逐步深入到系统调用、进程管理等核心话题,最后探讨内核在现代计算环境中的作用和面临的挑战。无论你是编程新手还是资深开发者,这篇文章都将为你提供新的视角和深刻的洞见。
|
7天前
|
安全
探索操作系统的心脏:内核与用户模式的交互之旅
【9月更文挑战第12天】在数字世界的海洋中,操作系统扮演着灯塔的角色,指引着每一条数据流的方向。本文将深入探讨操作系统的核心机制——内核与用户模式,揭示它们如何协同工作以保障计算机系统的高效与安全。我们将从基础概念出发,逐步深入到实际代码示例,旨在为读者呈现一幅清晰的操作系统工作原理图景。
|
10天前
|
传感器 安全 算法
操作系统的心脏:内核探秘
在数字世界的每一次跳动中,操作系统内核扮演着至关重要的角色。本文将深入浅出地探索内核的奥秘,从它的定义和功能出发,逐步解析其结构和原理,并探讨内核对系统性能的影响。最后,我们将展望未来内核技术的发展方向,以及它如何适应不断变化的技术需求。让我们一起揭开操作系统内核的神秘面纱,体验技术的魅力。
|
8天前
|
存储 算法 安全
操作系统的心脏:内核深入解析
本文将带您走进计算机的大脑—操作系统内核,探索它如何管理硬件资源、提供系统服务,并确保多任务高效运行。文章以浅显易懂的语言,逐步揭示内核的神秘面纱,从基础概念到实际应用,让您对操作系统的核心组件有更深的理解。
29 5
|
8天前
|
存储 安全 Linux
探索Linux操作系统的心脏:内核
在这篇文章中,我们将深入探讨Linux操作系统的核心—内核。通过简单易懂的语言和比喻,我们会发现内核是如何像心脏一样为系统提供动力,处理数据,并保持一切顺畅运行。从文件系统的管理到进程调度,再到设备驱动,我们将一探究竟,看看内核是怎样支撑起整个操作系统的大厦。无论你是计算机新手还是资深用户,这篇文章都将带你领略Linux内核的魅力,让你对这台复杂机器的内部运作有一个清晰的认识。
24 3
|
8天前
|
存储 安全 Linux
操作系统的心脏:内核探秘
在数字世界的庞大机器中,操作系统扮演着至关重要的角色,而其核心—内核则如同这台机器的心脏。本文将深入浅出地剖析操作系统内核的设计哲学、功能组成以及它如何管理硬件资源和提供系统服务。我们将一同探索进程调度、内存管理、文件系统等关键组件,并通过实例了解它们是如何协同工作以确保系统的高效与稳定。无论你是技术新手还是资深开发者,这篇文章都将为你打开一扇了解操作系统深邃世界的大门。
20 3