引导程序通过汇编初始化并切换到保护模式,加载C++内核代码,在屏幕上显示“Hello OS!”,实现裸机下C++运行。

编写操作系统内核听起来很复杂,但可以从最基础的一步开始:让计算机启动时运行你写的代码。这个过程的第一步就是写一个简单的x86引导程序(bootloader)。虽然C++是现代系统开发的重要工具,但在引导阶段,我们必须与硬件直接交互,因此需要结合汇编语言来完成初始化工作,然后才能进入C++环境。
当计算机加电后,BIOS会查找位于磁盘第一个扇区(512字节)的引导扇区。如果该扇区最后两个字节是签名 0x55 和 0xAA,BIOS就会将这段代码加载到内存地址 0x7C00 并跳转执行。
由于这只有512字节,且CPU此时处于实模式(16位),不能直接运行现代C++代码。我们需要先用汇编完成基本设置,再切换到保护模式,才能加载和执行C++编写的内核部分。
创建一个汇编文件,负责初始引导并跳转到C++代码:
[BITS 16] [ORG 0x7C00] <p>start: cli ; 关闭中断 xor ax, ax mov ds, ax ; 设置数据段 mov es, ax mov ss, ax mov sp, 0x7C00 ; 设置栈指针 sti ; 开启中断</p><pre class='brush:php;toolbar:false;'>; 切换到保护模式第一步:加载GDT lgdt [gdt_descriptor] mov eax, cr0 or eax, 1 mov cr0, eax ; 远跳转进入保护模式 jmp 0x08:protected_mode_entry
; 全局描述符表(GDT) gdt_start: dq 0 ; 空描述符 code_segment: dw 0xFFFF ; 段限长 dw 0 ; 基址低16位 db 0 ; 基址中8位 db 10011010b ; 标志位:代码段,可执行,只读 db 11001111b ; 段限高4位 + 标志 db 0 ; 基址高8位 data_segment: dw 0xFFFF dw 0 db 0 db 10010010b ; 数据段,可读写 db 11001111b db 0 gdt_end:
gdt_descriptor: dw gdt_end - gdt_start - 1 ; GDT大小 dd gdt_start ; GDT起始地址
; 保护模式入口点(链接时需匹配) [bits 32] protected_mode_entry: mov ax, 0x10 ; 加载数据段选择子 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0x90000 ; 设置栈
; 调用C++主函数 call kernel_main ; 防止退出
hang: hlt jmp hang
这段汇编完成了以下任务:
现在可以写第一个C++函数了。它将在保护模式下运行:
extern "C" void kernel_main() {
// 显存地址:0xB8000,文本模式下每字符占2字节(字符+属性)
unsigned char* video_memory = (unsigned char*)0xB8000;
<pre class='brush:php;toolbar:false;'>const unsigned char color = 0x07; // 灰底黑字
// 显示 "Hello OS!"
const char* message = "Hello OS!";
int i = 0;
while (message[i] != '\0') {
video_memory[i * 2] = message[i]; // 字符
video_memory[i * 2 + 1] = color; // 属性
i++;
}
// 死循环
while(1);}
注意:extern "C" 防止C++名字修饰,确保链接器能找到 kernel_main 符号。
你需要交叉编译工具链(如 gcc、nasm、ld)来生成纯二进制镜像。
独响
一个轻笔记+角色扮演的app
249
查看详情
步骤1:汇编start.asm
nasm -f bin start.asm -o boot.bin
步骤2:编译kernel.cpp(32位,无标准库)
g++ -m32 -ffreestanding -fno-exceptions -fno-rtti -c kernel.cpp -o kernel.o
步骤3:链接成单一内核镜像
创建一个链接脚本 linker.ld:
ENTRY(start)
SECTIONS {
. = 0x100000;
.text : {
*(.text)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}
执行链接:
ld -m elf_i386 -T linker.ld -o kernel.bin kernel.o
步骤4:合并引导扇区和内核
cat boot.bin kernel.bin > os-image.bin
使用QEMU运行:
qemu-system-i386 -fda os-image.bin
你应该看到屏幕显示 “Hello OS!”,这意味着你的C++代码已经成功在裸机上运行了。
基本上就这些。虽然这只是最简引导程序,但它为你后续实现内存管理、进程调度、文件系统等内核功能打下了基础。关键在于:汇编负责“启动舞台”,C++负责“表演内容”。
以上就是C++如何编写操作系统内核_从零开始用C++编写一个简单的x86引导程序的详细内容,更多请关注其它相关文章!
相关文章:
京东单号查询入口_京东快递订单追踪入口
PostgreSQL海量数据高效导入策略:Python与Django实践指南
《明末:渊虚之羽》设计师谈设计角色:那会刚毕业 充满激情
PHP:根据嵌套关联数组项值动态添加新键值对
c++如何使用std::memory_order控制原子操作顺序_c++ C++11内存模型详解
汽水音乐车机版8.9下载 汽水音乐车机版8.9版本安装入口
Golang如何通过reflect操作map_Golang reflect map操作与遍历技巧
文本文档写html代码怎么运行_文本文档html代码运行步骤【教程】
WooCommerce后台产品编辑页:获取分类ID并实现角色权限控制
C++如何操作注册表_Windows平台下C++读写注册表的API函数详解
AO3官方在线访问地址 Archive of Our Own最新镜像合集
LocoySpider如何部署到云服务器_LocoySpider云部署的远程配置
从OpenAI API响应中高效提取生成文本
mcjs网页版流畅运行 mcjs低配电脑畅玩入口
Windows10怎么开启存储感知 Windows10系统设置自动清理临时文件释放C盘空间【教程】
composer 和 npm/yarn 在管理依赖方面有什么核心思想差异?
AO3网页版最新入口合集 Archive of Our Own在线访问指南
微博网页版首页入口 微博电脑端官网登录链接
处理Kafka消费者会话超时:深入理解消息处理语义与幂等性
c++中的std::launder有什么实际用途_c++对象生命周期与指针优化
CSS条件样式无法按设备触发怎么排查_media条件语句正确设置解决触发问题
包子漫画官方网站在线链接-包子漫画在线阅读平台主页地址
手机CPU怎么影响游戏体验_手机CPU对游戏性能的影响分析
蓝湖怎样用切图标注提对接效率_蓝湖用切图标注提对接效率【设计对接】
知音漫客正版漫画平台_知音漫客官网账号登录
steam官方入口大全 steam账号注册及操作指南
处理Kafka消息时会话超时与实现幂等性消费者
Go语言HTML解析:利用Goquery精准获取指定元素内容
C++编译期如何执行复杂计算_C++模板元编程(TMP)技巧与应用
在Qt QML中通过Python字典动态更新TextEdit内容的教程
千牛数据看板网页版_千牛数据看板网页版访问方法
Win11怎么隐藏桌面图标 Win11一键隐藏所有桌面元素及恢复显示
使用J*aScript检测输入元素是否包含在特定类中
word中如何让数字纵向排列_Word数字纵向排列方法
KFC早餐时段怎么领特惠代码_KFC早餐订餐优惠代码获取与使用说明
如何高效处理PHP中的Excel数据导入导出?PortPHP/Spreadsheet助你轻松搞定!
php源码怎么看淘宝客系统_看php源码淘宝客系统技巧
C++如何生成随机数_C++ random库使用方法与范围设置
win11如何卸载Windows更新补丁 Win11解决更新导致系统不稳定的问题【修复】
解决Rails应用中内容错位与Turbo警告:meta标签误用导致富文本渲染异常
PHP表单隐藏域数据传递:常见问题与最佳实践
AO3镜像入口大全 AO3网页版内容访问全集
b站怎么看视频的弹幕数量_b站弹幕数量查看方法
解决 Express.js 中 PUT 请求密码修改失败的路由配置指南
神庙逃亡小游戏在线玩 神庙逃亡小游戏入口
Go语言中Map存储的结构体如何调用指针方法:深入解析与实践
苹果手机指南针不准怎么校准 传感器校准方法详解【建议收藏】
Go语言中JSON数据解码与字段访问指南
PyTorch模型训练效果不佳?深入剖析常见错误与调试技巧
优化 Python 函数中的条件逻辑:解决 if-else 嵌套与参数选择问题