Bran的内核开发教程(bkerndev)-03 内核初步
内核初步
在这节教程, 我们将深入研究一些汇编程序, 学习创建链接脚本的基础知识以及使用它的原因。最后, 我们将学习如何使用batch(批处理)文件自动汇编、编译和链接这个最基本的受保护模式下的内核。本教程假定你已经安装了NASM和GCC, 并且了解一点点x86汇编语言。
内核入口
内核的入口点是当引导程序(bootloader)调用内核时最先执行的代码段。这段代码一直以来几乎都是使用汇编编写的, 因为有些工作如设置新的栈, 加载新的GDT、IDT或寄存器, 你简单地使用C语言根本没法做到。在很多初学者写的内核, 和更专业的内核中, 会将所有汇编程序代码放在一个文件中, 并将其余源代码分别放在几个C文件中。
如果至少知道一点点汇编语言, 那么下面这段汇编代码应该非常简单明了了。就代码而言, 这个文件做的只有加载一个新的8KB栈, 然后跳转到一个死循环中。这个栈是一块很小的内存, 它用于存储或传递参数给C函数。它还可以用来保存你函数中声明和使用的局部变量。其他的全局变量则存储在BSS区域中。在mboot
和stublet
代码块之间的代码用于生成特殊的签名, GRUB通过该签名校验即将加载的二进制输出文件, 实际上该文件就是内核。不过不用费力去理解多重引导头(multiboot header)。
内核启动文件“start.asm”的内容如下:
start.asm
; 这是内核的入口点. 我们将在这里调用main函数,并设置栈和其他东西,比如:
; 创建GDT和内存区域,
; 请注意这里中断被禁用了,更多细节将在后面讲中断的时候提到
[BITS 32]
global start
start:
mov esp, _sys_stack ; 让当前栈指针指向我们新创建的栈
jmp stublet ; 跳转到stublet
; 使用'ALIGN 4'使这段代码4字节对齐
ALIGN 4
mboot:
; 多重引导的一些宏定义,使后面一些代码更具可读性
MULTIBOOT_PAGE_ALIGN equ 1<<0
MULTIBOOT_MEMORY_INFO equ 1<<1
MULTIBOOT_AOUT_KLUDGE equ 1<<16
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
EXTERN code, bss, end
; GRUB 多重引导头 - 启动签名
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_CHECKSUM
; 自动替换 - 必须为物理地址
; 注意:由链接器填充这些数据的值
dd mboot
dd code
dd bss
dd end
dd start
; 死循环
; 之后我们将在'jmp $'前插入'extern _main'和'call _main'两句代码
stublet:
jmp $
; GDT加载代码(以后添加)
; ISR代码(以后添加)
; BSS区的定义
; 现在问将用它来存储栈
; 栈是向下生长的,所以我们在声明'_sys_stack'
SECTION .bss
resb 8192 ; 保留8KB内存
_sys_stack:
链接脚本
链接器是接收所有编译器和汇编器的输出文件, 并把它们链接成一个二进制文件的工具。二进制文件有很多种格式, 常见的有: Flat、AOUT、COFF、PE、ELF等。我们在工具集中选择的链接器是LD链接器。这是一个非常好的多功能链接器。LD链接器有多种版本, 可以输出任何你想要格式的位二进制文件。无论你选择那种输出格式, 输出的文件总由3个部分组成: 1) 'Text'或'Code'是可执行段;2) 'Data'段用于存放硬编码值(hardcoded value), 比如你声明了一个变量, 并将该变量的值设为5, 这个'5'就被存储在'Data'区域;3) 'BSS'段由未初始化的数据组成, 如没有赋值的数组。'BSS'是一个虚拟段, 它在二进制镜像中是不存在的, 但是在二进制文件加载的时候存在于内存中。
下面是LD链接脚本文件"link.ld"的内容。OUTPUT_FORMAT
关键字告诉LD我们将创建哪种形式的二进制镜像, 简单起见, 我们使用'binary'二进制镜像。ENTRY
用于指定哪个目标文件最先被链接。我们希望”start.asm“编译后的输出文件”start.o“为第一个链接的目标文件, 也就是没和的入口点。phys
不是关键字, 而是链接脚本中使用的变量, 被用来指向内存中1MB地址的指针, 也就我们二进制文件被加载和运行的地方。SECTIONS
里定义了3个主要区域: '.text'、'.data'、'.bss', 并同时定义了3个变量: 'code', 'data', 'bss', 还有 'end'。不要对此感到困惑, 其实这三个变量是我们的启动文件"start.asm"中的变量。ALIGN(4096)
用来确保每个区域以4096B(4KB)为边界。在这种情况下, 每个部分将从内存中的单独"页"开始。
link.ld
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
汇编和链接
这里我们必须对"start.asm"进行汇编, 并使用上面的链接脚本来创建我们内核的二进制文件, 以便GRUB加载。在Unix系统中实现上述操作的最简单的方法就是创建一个makefile脚本文件来帮你汇编、编译、链接。但是大多数人使用的是Windows系统, 在Windows系统中, 我们可以创建一个batch文件。batch文件其实就是DOS命令的集合, 你可只需输入这个batch文件的文件名就可以依次执行该batch文件中的DOS命令集。更简单的方法是, 你只需要双击该batch文件, 就会在Windows系统下自动执行DOS命令编译你的内核。
下面是本教程使用的batch文件"build.bat"。echo
是一个DOS命令, 用来向终端显示字符。nasm
是我们使用的汇编器, 我们以aout的格式编译, 因为LD链接器需要一种已知格式才能解析链接过程中的符号。汇编器将’start.asm'汇编成'start.o'。rem
命令是注释, 在运行batch文件是会将它忽略。ld
是我们的链接器, '-T'参数告诉链接器我们使用哪一个链接脚本, -o
用来指定输出文件名。其他的参数都将被链接器理解为需要链接到一起并解析生成kernel.bin的文件。最后, pause
命令将在屏幕上显示"Press a key to continue..."并等待我们按下键盘上的任意键, 这方便我们查看汇编器或链接器在语法错误上给出了哪些提示。
build.bat
echo Now assembling, compiling, and linking your kernel:
nasm -f aout -o start.o start.asm
rem Remember this spot here: We will add 'gcc' commands here to compile C sources
rem This links all your files. Remember that as you add *.o files, you need to
rem add them after start.o. If you don't add them at all, they won't be in your kernel!
ld -T link.ld -o kernel.bin start.o
echo Done!
pause
PS: 下面是我自己写的
64位Linux下的编译脚本
build.sh
echo "Now assembling, compiling, and linking your kernel:"
nasm -f elf64 -o start.o start.asm
# Remember this spot here: We will add 'gcc' commands here to compile C sources
# This links all your files. Remember that as you add *.o files, you need to
# add them after start.o. If you don't add them at all, they won't be in your kernel!
ld -T link.ld -o kernel.bin start.o
echo "Done!"
使用下面指令运行:
bash build.sh
此文原创禁止转载,转载文章请联系博主并注明来源和出处,谢谢!
作者: Raina_RLN https://www.cnblogs.com/raina/
Bran的内核开发教程(bkerndev)-03 内核初步的更多相关文章
- Bran的内核开发教程(bkerndev)-02 准备工作
准备工作 内核开发是编写代码以及调试各种系统组件的漫长过程.一开始这似乎是一个让人畏惧的任务,但是并不需要大量的工具集来编写自己的内核.这个内核开发教程主要涉及使用GRUB将内核加载到内存中.GR ...
- Bran的内核开发教程(bkerndev)-01 介绍
介绍 内核开发不是件容易的事,这是对一个程序员编程能力的考验.开发内核其实就是开发一个能够与硬件交互和管理硬件的软件.内核也是一个操作系统的核心,是管理硬件资源的逻辑. 处理器或是CPU是内核 ...
- Bran的内核开发教程(bkerndev)-04 创建main函数和链接C文件
目录 创建main函数和链接C文件 PS: 下面是我自己写的 Win10安装gcc编译器 本节教程对应的Linux下的编译脚本 _main的问题 创建main函数和链接C文件 一般C语言使用mai ...
- Bran的内核开发教程(bkerndev)-08 中断服务程序(ISR)
中断服务程序(ISR) 中断服务程序(ISR)用于保存当前处理器的状态, 并在调用内核的C级中断处理程序之前正确设置内核模式所需的段寄存器.而工作只需要15到20行汇编代码来处理, 包括调用C中的 ...
- Bran的内核开发教程(bkerndev)-06 全局描述符表(GDT)
全局描述符表(GDT) 在386平台各种保护措施中最重要的就是全局描述符表(GDT).GDT为内存的某些部分定义了基本的访问权限.我们可以使用GDT中的一个索引来生成段冲突异常, 让内核终止执行异 ...
- Bran的内核开发教程(bkerndev)-07 中断描述符表(IDT)
中断描述符表(IDT) 中断描述符表(IDT)用于告诉处理器调用哪个中断服务程序(ISR)来处理异常或汇编中的"int"指令.每当设备完成请求并需要服务事, 中断请求也会调用I ...
- Bran的内核开发教程(bkerndev)-05 打印到屏幕
打印到屏幕 现在, 我们需要尝试打印到屏幕上.为此, 我们需要管理屏幕滚动, 如果能允许使用不同的颜色就更好了.好在VGA视频卡为我们提供了一片内存空间, 允许同时写入属性字节和字符字节对, 可以 ...
- 内核开发知识第一讲.内核中的数据类型.重要数据结构.常用内核API函数.
一丶内核中的数据类型 在内核中.程序的编写不能简单的用基本数据类型了. 因为操作系统不同.很有可能造成数据类型的长度不一.而产生重大问题.所以在内核中. 数据类型都一定重定义了. 数据类型 重定义数据 ...
- Linux内核设计基础(十)之内核开发与总结
(1)Linux层次结构: (2)Linux内核组成: 主要由进程调度(SCHED).内存管理(MM).虚拟文件系统(VFS).网络接口(NET)和进程间通信(IPC)等5个子系统组成. (3)与Un ...
随机推荐
- android 滚动时间选择器
一.概述 滚动时间选择现在貌似很常用,所以就总结一下,显示效果一般般 , 做个参考吧! 以上就是效果图,可以滚动选择 日期时间, 由于是在 5.0系统运行的,貌似5.0系统做了什么变动,下面的 &qu ...
- 使用ant编译web工程步骤
1.把写好的web工程拷贝到tomcat7.x下的webapps目录中 2.如果已经安装过ant的在doc下面直接输入:ant compile编译工程,如果成功最后会有BUILD SUCCESSFUL ...
- 【第十六篇】这一次要写的是bootstrap-table
先上图吧这就是效果图 上代码(这一部分是工具栏的,还包括slider滑动条) <div class="box-body"> <div class="ro ...
- 【第十二篇】微信支付(APP)集成时碰到的问题(.net提示“无权限”、iOS跳转到微信支付页面中间只有一个“确定”按钮)(转)
直入主题之前,请容我吐槽一下微*的官方东西:ASDFQ%#$%$#$%^FG@#$%DSFQ#$%.......:吐槽玩了!大家心照就好. 要完成手机APP跳转到微信的APP进行微信支付,需要进行如下 ...
- Qt插件热加载-QPluginLoader实现
上一篇C++消息框架-基于sigslot文章中我们讲述了使用sigslot信号槽实现自己的消息框架,这是一个比较粗糙,而且小的框架.当我们的程序逐渐变大时,我们可能就会考虑功能插件化,或者支持某些模块 ...
- 搭建Android+QT+OpenCV环境,实现“单色图片着色”效果
OpenCV是我们大家非常熟悉的图像处理开源类库:在其新版本将原本在Contrib分库中的DNN模块融合到了主库中,并且更新了相应文档.这样我们就能够非常方便地利用OpenCV实 ...
- 不fq安装 golang tools
go get -u -v github.com/golang/tools/go/buildutil ln -s $GOPATH/src/github.com/golang/tools $GOPATH/ ...
- Django REST Framework序列化器
Django序列化和json模块的序列化 从数据库中取出数据后,虽然不能直接将queryset和model对象以及datetime类型序列化,但都可以将其转化成可以序列化的类型,再序列化. 功能需求都 ...
- python + selenium 环境搭建及问题
搭建平台windows 准备工具如下: ------------------------------------------------------------- 下载python https://w ...
- 如何免费使用GPU跑深度学习代码
从事深度学习的研究者都知道,深度学习代码需要设计海量的数据,需要很大很大很大(重要的事情说三遍)的计算量,以至于CPU算不过来,需要通过GPU帮忙,但这必不意味着CPU的性能没GPU强,CPU是那种综 ...