栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

DellaOS内核程序篇(更新中)

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

DellaOS内核程序篇(更新中)

DellaOS内核程序篇

本文参照《Orange’S:一个操作系统的实现》与《一个64位操作系统的设计与实现》实现(部分内容有删改),如有描述不清或错误处,请阅读原著或联系本人

在学习过程中,由于操作系统知识过于庞大,我们奉行”懒加载“的原则,用到什么学习什么

2021-11-25

1、引导篇的疑惑

在引导篇中,我们仍有许多疑惑

  • 为什么要把内核移至0x100000处
  • 为什么DELLA_KERNEL_PARAMETER_INFORMATION要放在0x60000处
  • 页表中的frameBuffer地址映射更新为什么出现那么多特别的数字
  • 物理地址空间信息为什么要转为E820内存结构

接下来我们将一点点拨开眼前的迷雾

2、核心工作
  • 日志打印
  • 异常处理
  • 内存管理
  • 中断处理
  • 键盘驱动
  • 进程管理
  • 文件系统
  • API库
  • Shell
3、基础知识学习 3.1、环境搭建

环境依赖

sudo yum install gtk2 gtk2-devel libXt libXt-devel libXpm libXpm-devel SDL SDL-devel xorg-x11-server-devel nasm gcc-c++ glibc-headers libX11-devel libXrandr-devel lrzsz -y

安装bochs-2.6.8

tar -xvf bochs-2.6.8.tar.gz
cd bochs-2.6.8
./configure --with-x11 --with-wx --enable-debugger --enable-disasm 
                --enable-all-optimizations --enable-readline --enable-long-phy-address 
                --enable-ltdl-install --enable-idle-hack --enable-plugins --enable-a20-pin 
                --enable-x86-64 --enable-smp --enable-cpu-level=6 --enable-large-ramfile 
                --enable-repeat-speedups --enable-fast-function-calls   
                --enable-handlers-chaining  --enable-trace-linking 
                --enable-configurable-msrs --enable-show-ips --enable-cpp 
                --enable-debugger-gui --enable-iodebug --enable-logging 
                --enable-assert-checks --enable-fpu --enable-vmx=2 --enable-svm 
                --enable-3dnow --enable-alignment-check  --enable-monitor-mwait 
                --enable-avx  --enable-evex --enable-x86-debugger --enable-pci 
                --enable-usb --enable-usb-ohci --enable-usb-ehci   --enable-usb-xhci  
                --enable-voodoo
cp misc/bximage.cpp misc/bximage.cc
cp iodev/hdimage/hdimage.cpp iodev/hdimage/hdimage.cc
cp iodev/hdimage/vmware3.cpp iodev/hdimage/vmware3.cc
cp iodev/hdimage/vmware4.cpp iodev/hdimage/vmware4.cc
cp iodev/hdimage/vpc-img.cpp iodev/hdimage/vpc-img.cc
cp iodev/hdimage/vbox.cpp iodev/hdimage/vbox.cc
make  && make install
3.2、Makefile学习 3.2.1、基本格式
target: prerequisites
	command
  • target:目标文件
  • prerequisites:先决条件(依赖条件)
  • command:make需要执行的命令 (任意的shell命令,必须以[tab]开头)
3.2.2、通配符
  • * :表示任意一个或多个字符
  • ? :表示任意一个字符
  • […]:[abcd] 表示a,b,c,d中任意一个字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一个数字
  • ~ :表示用户的home目录
3.2.3、变量

:= 只能使用前面定义好的变量, = 可以使用后面定义的变量

1、变量定义、使用

object1 = programA.o programB.o
object2 = $(object) programC.o
all:
    @echo "object2: " $(object2)
# object2 = programA.o programB.o programC.o

2、变量替换

object1 := programA.c programB.c programC.c
object2 := $(object1:%.c=%.o)
all:
    @echo "object2: " $(object2)
# object2 = programA.o programB.o programC.o

3、变量追加值

object1 = programA.c programB.c programC.c
object2 += programD.c
all:
    @echo "object2: " $(object2)
# object2 = programA.c programB.c programC.c programD.c

4、override

使 Makefile中定义的变量能够覆盖 make 命令参数中指定的变量

object1 = programA.c programB.c

# ============= 命令:make object1=programC.c
# object1=programC.c
override object2 = programA.c programB.c
# ============= 命令:make object2=programC.c 
# object2=programA.c programB.c

5、目标变量

target1: object1 = programA.c
target1:
	@echo "object1: " $(object1)
# object1 = programA.c
target2:
	@echo "object1: " $(object1)
# object1:
3.2.4、命令前缀
  • 不用前缀:输出执行的命令以及命令执行的结果, 出错的话停止执行
  • 前缀@:只输出命令执行的结果, 出错的话停止执行
  • 前缀-:命令执行有错的话, 忽略错误, 继续执行
3.2.5、伪目标

伪目标并不是一个"目标(target)",不像真正的目标那样会生成一个目标文件

# .PHONY没有也行, 但是最好加上
.PHONY: clean
clean:
    -rm -f *.o
3.2.6、引用Makefile

include (filename 可以包含通配符和路径)

3.2.7、自动变量
自动变量含义
$@目标集合
$%当目标是函数库文件时, 表示其中的目标文件名
$<第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标
$?比目标新的依赖目标的集合
$^所有依赖目标的集合, 会去除重复的依赖目标
$+所有依赖目标的集合, 不会去除重复的依赖目标
$*这个是GNU make特有的, 其它的make不一定支持
3.2.8、函数
$( )
${ }

常用函数

  • 字符串替换函数

    # 把text中的from字符串换位to字符串
    $(subst ,,)
    
  • 模式字符串替换函数(可以使用%去匹配任意长度字符串)

    $(patsubst ,,)
    
  • 字符串查找函数

    # 在in字符串中寻找find字符串
    $(findstring ,)
    
  • 过滤函数

    $(filter ,)
    
  • 反过滤函数

    $(filter-out ,)
    
  • 排序函数

    $(sort )
    
  • 取单词函数

    $(word ,)
    
  • 取单词串函数

    $(word ,,)
    
  • 单词个数统计

    $(words )
    
  • 首单词函数

    $(firstword )
    
  • 取目录函数

    $(dir )
    
  • 取文件名函数

    $(notdir )
    
  • 取后缀函数

    $(suffix )
    
  • 取前缀函数

    $(basename )
    
  • 加后缀函数

    $(addsuffix ,)
    
  • 加前缀函数

    $(addprefix ,)
    
  • 循环函数

    $(foreach ,,)
    
  • 条件判断函数

    $(if ,)
    $(if ,,)
    $(ifdef)
    $(ifndef)
    
  • 判断变量的来源

    $(origin )
    
    类型含义
    undefined 没有定义过
    default 是个默认的定义, 比如 CC 变量
    environment 是个环境变量, 并且 make时没有使用 -e 参数
    file 定义在Makefile中
    command line 定义在命令行中
    override 被 override 重新定义过
    automatic 是自动化变量
3.2.9、ifeq,ifneq(注意缩进!!!,最好在vim下)
all:
ifeq ("a","b")
	echo "equal"
else
	echo "not equal"
endif
# echo "not equal"
# not equal
3.2.10、约定
伪目标含义
all所有目标的目标,其功能一般是编译所有的目标
clean删除所有被make创建的文件
install安装已编译好的程序,其实就是把目标可执行文件拷贝到指定的目录中去
print列出改变过的源文件
tar把源程序打包备份. 也就是一个tar文件
dist创建一个压缩文件, 一般是把tar文件压成Z文件. 或是gz文件
TAGS更新所有的目标, 以备完整地重编译使用
check 或 test一般用来测试makefile的流程
3.3、编译命令学习 3.3.1、as

as命令是gcc套件中的汇编器,它采用的是AT/T的汇编语法

格式为as [选项] 汇编文件

  • --32/--64。它生成32/64位代码
  • -o OBJFILE。它将编译生成的目标二进制程序段保存在OBJFILE文件内,OBJFILE的默认文件名为a.out
as --64 -o entry.o entry.s
# 把汇编源文件entry.s编译成64位的二进制程序段,并将其保存在文件entry.o里
# 之所以将entry.o文件称为二进制程序段而不是二进制程序,是因为entry.o文件不是可执行文件,它需要经过链接后,才能成为可执行程序
3.3.2、gcc

格式为gcc [选项] 文件

  • -E。它使编译器只执行预处理过程,不执行编译、汇编、链接等过程,也不会生成目标文件,此时需要将源文件预处理的结果重定向到一个输出文件中
  • -C。在预处理过程中,它不删除注释信息,通常情况下和-E联合使用
  • -mcmodel=large。mcmodel用于限制程序访问的地址空间,选项large表明程序可访问任何虚拟地址空间,其他选择无法使程序访问整个虚拟地址空间
  • -fno-builtin。除非使用前缀__builtin_明确引用,否则编译器不识别所有系统内建函数。常见的系统内建函数有alloca、memcpy等
  • -m32/-m64。它生成32/64位代码
  • -c。它执行预处理、编译、汇编等过程,但不执行链接过程
gcc -E  entry.S > entry.s
# 对汇编源文件entry.S进行预处理,同时将预处理的结果重定向(导出)到目标文件entry.s中
gcc  -mcmodel=large -fno-builtin -m64 -c main.c
# 将main.c编译成64位的二进制程序段文件main.o。与此同时,这条命令还不限制地址空间的访问范围、不识别所有系统内建函数。
3.3.3、ld

格式为ld [选项] 文件

  • -b TARGET。它指定输入文件的文件格式。ld命令支持的文件格式有:elf64-x86-64、elf32-i386、a.out-i386-linux、pei-i386、pei-x86-64、elf64-l1om、elf64-little、elf64-big、elf32-little、elf32-big、srec、symbolsrec、verilog、tekhex、binary、ihex等。(可通过help选项查询ld命令支持的文件格式)

  • -z muldefs。它允许重复定义。当遇见重复定义时,编译器只使用其中一个

  • -o FILE。它指定输出文件的文件名

  • -T FILE。它为链接过程提供链接脚本文件

ld -b elf64-x86-64 -z muldefs -o system head.o entry.o main.o printk.o trap.o memory.o interrupt.o task.o -T Kernel.lds
# 将head.o、entry.o、main.o等编译好的二进制程序段文件按照链接脚本kernel.lds的描述对程序段进行部署,最终将它们链接成elf64-x86-64文件格式的可执行程序system,这个链接过程会过滤掉重复定义的函数
3.3.4、objcopy

格式为objcopy [选项] 输入文件 [输出文件]

  • -I TARGET。它指定输入文件的文件格式。objcopy命令支持的文件格式有:elf64-x86-64、elf32-i386、a.out-i386-linux、pei-i386、pei-x86-64、elf64-l1om、elf64-little、elf64-big、elf32-little、elf32-big、srec、symbolsrec、verilog、tekhex、binary、ihex等。(可通过help选项查询objcopy命令支持的文件格式)
  • -S。它移除所有symbol和relocation信息
  • -R name。它从输出文件中移除名为name的程序段
  • -O TARGET。它指定输出文件的文件格式
 objcopy -I elf64-x86-64 -S -R ".eh_frame" -R ".comment" -O binary system kernel.bin
 # 这条命令的作用是移除可执行程序system中的所有`symbol`和`relocation`信息,并移除名为.eh_frame和.comment的程序段,最后将剩余程序段以二进制格式输出到文件kernel.bin中
3.4、链接脚本学习

链接脚本的主要作用是描述如何将输入文件中的各程序段(数据段、代码段、堆、栈、BSS)部署到输出文件中,并规划输出文件各程序段在内存中的布局

OUTPUT_FORMAT("elf64-x86-64","elf64-x86-64","elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SECTIONS
{

    . = 0xffff800000000000 + 0x100000;
    .text :
    {
        _text = .;
        *(.text)
        _etext = .;
    }

    . = ALIGN(8);

    .data:
    {
        _data = .;
        *(.data)
        _edata = .;
    }

    .rodata:
    {
        _rodata = .;
        *(.rodata)
        _erodata = .;
    }

    . = ALIGN(32768);
    .data.init_task : { *(.data.init_task) }

    .bss :
    {
        _bss = .;
        *(.bss)
        _ebss = .;
    }

    _end = .;
}
  • OUTPUT_FORMAT(DEFAULT,BIG,LITTLE):为链接过程提供三种输出文件格式,若链接命令使用-EB选项,那么程序将链接成BIG指代的文件格式;如果链接命令中有-EL选项,那么程序将链接成LITTLE指代的文件格式。否则程序将链接成默认文件格式,即DEFAULT指代的文件格式
    • DEFAULT(默认)
    • BIG(大端)
    • LITTLE(小端)
  • OUTPUT_ARCH(BFDARCH):指定输出文件的处理器体系结构
  • ENTRY(SYMBOL):将标识符SYMBOL设置为程序的入口地址,即执行程序的第一条指令所在地址
  • SECTIONS:SECTIONS关键字负责向链接器描述如何将输入文件中的各程序段(数据段、代码段、堆、栈、BSS)部署到输出文件中,同时还将规划各程序段在内存中的布局
  • 符号.是一个定位器或位置指针,它用于定位程序的地址或调整程序的布局位置
  • 链接脚本中的正则表达式*(.text) ,说明了输出文件的.text程序段保存着所有输入文件的.text程序段。而且,.text程序段还使用了_text和_etext标识符来标示.text程序段的起始线性地址和结尾线性地址,这两个标识符可在程序中通过代码extern _text和extern _etext进行引用(将它们看作全局变量)
  • ALIGN(NUM):将地址向后按NUM字节对齐
3.5、地址空间

学习处理器的运行模式前,让我们先来了解一些有关地址空间的概念

地址空间在一般情况下主要分为两大类:虚拟地址空间和物理地址空间。而虚拟地址空间又可分为:逻辑地址、有效地址、线性地址等

3.5.1、虚拟地址
  • 逻辑地址(Logical Address):在操作系统的研发过程中,逻辑地址经常会使用到,它的书写格式为Segment:Offset
  • 线性地址(Linear Address)。线性地址是通过逻辑地址中的段基地址与段内偏移地址组合而成,这使得程序无法直接访问线性地址,平坦地址(Flat Address)作为一种特殊的线性地址,将段基地址和段长度覆盖了整个线性地址空间,而非线性地址空间中的某一部分区域

对于繁琐复杂的段管理机制而言,采用平坦地址可将段管理机制的地址转换过程透明化,即当段基地址为0时,段内偏移地址(有效地址)与线性地址在数值上相等

3.5.2、物理地址

物理地址(Physical Address)是真实存在于硬件设备上的,它通过处理器的引脚直接或间接地与外部设备、RAM、ROM相连接。因此,物理地址空间中不仅包含物理内存(RAM、ROM)还有硬件设备。在处理器开启分页机制的情况下,线性地址需要经过页表映射才能转换成物理地址;否则线性地址将直接映射为物理地址

3.6、IA-32e模式

IA-32e模式在原有32位保护模式的基础上进行了诸多升级、改造与整合,可近似视为一种全新的64位处理器体系结构

3.6.1、IA-32e模式概述

虽然IA-32e模式的线性地址位宽64位,但其线性寻址能力只有48位,其低48位用于线性地址寻址,高16位将作为符号扩展(将第47位数值扩展至第63位,即全为0或全为1),此种格式的地址被称为Canonical地址。IA-32e模式下,只有Canonical地址空间是可用地址空间,而Non-Canonical空间则属于无效地址空间

此时你应该明白为什么内核层的起始线性地址为0xffff800000000000

当64位模式采用Canonical型的64位线性地址后,页管理机制也改成4级,但只有线性地址的低48位参与页表空间检索,高16位(符号扩展位)依然不参与页表空间检索。而且,页管理机制在支持4 KB物理页的基础上,还支持2 MB和1 GB的物理页

3.6.2、IA-32e模式的段管理机制

代码段描述符

位图范围IA-32e模式位功能数值(HEX)数值解释
0~15忽略0000忽略
16~31忽略0000忽略
32~39忽略00忽略
40~43Type区域8非一致性、不可读、未访问
44S1代码段
45~46DPL000特权级
47P1已在内存中
48~51忽略0忽略
52AVL0忽略
53L164位工作模式
54D/B0
55G0忽略
56~63忽略00忽略

数据段描述符

位图范围IA-32e模式位功能数值(HEX)数值解释
0~15忽略0000忽略
16~31忽略0000忽略
32~39忽略00忽略
40~43Type区域2向上扩展、可读写、未访问
44S1数据段
45~46DPL000特权级
47P1已在内存中
48~51忽略0忽略
52AVL0忽略
53L0忽略
54D/B0忽略
55G0忽略
56~63忽略00忽略

系统段描述符(Type区域)

Type区域功能
43424140
000016 B描述符的高8 B
0001保留
0010LDT段描述符
0011保留
0100保留
0101保留
0110保留
0111保留
1000保留
100164位TSS段描述符(有效的)
1010保留
101164位TSS段描述符(使用中)
110064位调用门描述符
1101保留
111064位中断门描述符
111164位陷阱门描述符
3.7、IA-32e模式系统段描述符介绍 3.7.1、LDT段描述符

IA-32e模式的LDT段描述符共占用16 B的内存空间,其与TSS描述符的位功能完全相同

3.7.2、TSS段描述符

3.7.3、调用门描述符

调用门描述符同样从原有的8 B扩展至16 B,其低8 B的Param Count位区域已被忽略,而高8 B则保存着程序入口地址的第31~63位

3.8、IA-32e模式中断/异常处理机制

由于IA-32e模式的系统段描述符已不再支持任务门描述符,那么IDT仅剩下陷阱门描述符和中断门描述符可以使用

这两个门描述符均从8 B扩展至16 B,它的高8 B保存着Offset区域的第32~63位,不仅如此,其低8 B的第32~34位将用于IST功能

IST只在IA-32e模式下有效,它为了给不同的中断提供一个理想的栈环境,而对原有栈切换机制进行了改良。程序通过IST功能可使处理器无条件进行栈切换。在IDT的任意一个门描述符都可以使用IST机制或原有栈切换机制,当IST=0时,使用原有栈切换机制,否则使用IST机制

IA-32e模式的TSS已为IST机制提供了7个栈指针,供IDT的门描述符使用。图6-35中的IST位区域(共3位)就用于为中断/异常处理程序提供IST栈表索引,当确定目标IST后处理器会强制将SS段寄存器赋值为NULL段选择子,并将中断栈地址加载到RSP寄存器中。最后,将原SS、RSP、RFLAGS、CS和RIP寄存器值压入新栈中

3.9、IA-32e模式页管理机制

开启IA-32e模式必须伴随着页管理机制的开启(置位CR0.PG、CR4.PAE以及IA32_EFER.LME标志位)

IA-32e模式的页管理机制可将Canonical型的线性地址映射到52位物理地址空间(由处理器最高物理可寻址位宽值MAXPHYADDR决定)中,使得IA-32e模式可寻址4 PB(252 B)的物理地址空间,可寻址256 TB(248 B)的线性地址空间。处理器通过CR3控制寄存器保存的物理地址,可将线性地址转换成一个多层级页表结构,IA-32e模式的页管理机制共支持4 KB、2 MB和1 GB

248=221*29*29*2^9

​ =2MB*512*1024*256

接下来,正式开启"hello,kernel!"之旅

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/599362.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号