回答
如何在没有运行操作系统的情况下自行运行程序?
将二进制代码放置到处理器重新引导后寻找的位置(例如,ARM上的地址0)。
您可以创建计算机可以在启动时加载和运行的汇编程序(例如,从闪存驱动器启动计算机并运行驱动器上的程序)?
问题的一般答案:可以做到。 它通常被认为是“裸机编程”。 要从闪存驱动器中读取数据,您想知道USB是什么,并且您希望有一些驱动程序可以使用此USB。此驱动器上的程序也必须采用某种特定格式。在某些特定的文件系统上...这通常是引导加载程序所做的事情。 许多ARM板让你做这些事情。有些引导加载程序可以帮助您进行基本设置。
Here您可能会发现有关如何在Raspberry PI上执行基本操作系统的绝佳教程。
编辑: 这篇文章,整个wiki.osdev.org将ANWER大多数问题的 http://wiki.osdev.org/Introduction
另外,如果你不想直接在硬件上做实验,你可以运行它作为一个虚拟机使用像qemu这样的hypervisor。了解如何直接在虚拟化ARM硬件here上运行“hello world”。
可运行实例
技术上,如果没有一个OS上运行的程序,是一种操作系统。那么让我们看看如何创建和运行一些微不足道的Hello World操作系统。
以下所有示例的代码出现在this GitHub repo上。所有测试都在Ubuntu 14.04 AMD64 QEMU和真正的硬件ThinkPad T430上进行测试。
引导扇区
在x86上,最简单,最低级的东西你可以做的是创造一个Master Boot Sector (MBR),这是一种类型的boot sector,然后将其安装到硬盘。
实施例:
printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img
main.img
包含以下内容:
在八进制==
0xf4
\364
十六进制:用于hlt
指令,它告诉CPU停止工作的编码。因此,我们的程序不会做任何事情:只能开始和停止。
我们使用八进制,因为
\x
十六进制数不是由POSIX指定的。我们可以从容易得到本编码:
echo hlt > a.asm nasm -f bin a.asm hd a
,但它也存在于课程的英特尔手册。
%509s
产生509个空格。所需要的文件,以填补直到八进制==0x55
其次0xaa
字节510\125\252
:由硬件所需魔术字节。它们必须是字节511和512.如果不存在,硬件不会将其视为可引导磁盘。
请注意,即使没有做任何事情,几个字符已经打印在屏幕上。这些由固件打印,并用于识别系统。在真实的硬件
运行
仿真器是有趣的,但硬件是实打实的。
图像刻录到一个USB记忆棒(会破坏您的数据!):
sudo dd if=main.img of=/dev/sdX
插上USB电脑
打开它在
告诉它从USB启动。
这意味着使固件在硬盘之前选择USB。
如果这不是您机器的默认行为,请在开机后继续按Enter,F12,ESC或其他此类怪异按键,直到您获得可从USB启动的启动菜单。
通常可以在这些菜单中配置搜索顺序。
世界,你好
现在,我们已经做了一个最小的方案,让我们继续前进到一个Hello World。
显而易见的问题是:如何做IO?有几个选项:
这里我们来做一个BIOS例子,因为它比较简单。但请注意,这不是最稳健的方法。
这里是GAS代码:
.code16
.global _start
_start:
cli
mov $msg, %si
mov $0x0e, %ah
loop:
lodsb
or %al, %al
jz halt
int $0x10
jmp loop
halt:
hlt
msg:
.asciz "hello world"
.org 510
.word 0xaa55
除了标准的用户级汇编指令,我们有:
.code16
:告诉气体输出16位码cli
:禁用软件中断。这些可以使处理器在hlt
int $0x10
之后再次开始运行:进行BIOS调用。这是一个一个地打印字符。.org 510
和.word 0xaa55
:发生在年底
一个快速和肮脏的方式来编译这个神奇的字节是:
as -o main.o main.S
ld --oformat binary -o main.ing -Ttext 0x7C00 main.o
,并像以前一样运行main.img
。
这里有两个重要标志:
--oformat binary
:输出原始的二进制汇编代码,不要扭曲它的ELF文件中的是定期的userland可执行文件的情况。-Ttext 0x7C00
:我们需要告诉链接器ld
代码的放置位置,以便它能够访问内存。特别是在搬迁阶段使用。阅读更多关于它here。
更好的编译方式是使用干净的链接描述文件as this one。链接器脚本也可以为我们放置魔术字节。
固件
说实话,你的引导扇区是不是系统的CPU上运行的软件。
实际运行什么首先是所谓的固件,这是一个软件:
- 由硬件制造商
- 通常封闭源代码,但做可能基于C
- 存储在读只有记忆,因此在未经供应商同意的情况下难以/不可能修改。
BIOS是旧的全能x86固件,UEFI是它的新版本。
QEMU带有自己的BIOS固件实施。
固件做的事情,如:
循环每个硬盘,USB,网络等,直到你找到的东西引导。
当我们运行QEMU,
-hda
说main.img
是硬盘连接到硬件,hda
要尝试的第一个,它被使用。负荷第512个字节RAM的内存地址
0x7c00
,把CPU的RIP那里,让它在显示器上运行秀之类的东西在引导菜单或者BIOS打印通话
固件提供了OS-es所依赖的类OS功能。
固件甚至可以被认为是OS-es本身。例如。 Python的子集已经被移植到BIOS/UEFI运行:https://www.youtube.com/watch?v=bYQ_lq5dcvM
初始状态
像硬件很多事情,标准化是弱,你应该不依靠的事情之一是寄存器的初始状态。
所以请你帮个忙,并使用一些初始化代码如下所示:https://stackoverflow.com/a/32509555/895245
寄存器像%ds
和%es
有严重的副作用,所以你应该,即使你不使用它们明确零出来。
请注意,有些仿真器比真实硬件好,并给你一个很好的初始状态。然后当你在真正的硬件上运行时,一切都会中断。
GRUB多引导
引导扇区是简单的,但他们都不是很方便:
- 你只能有每个磁盘一个OS
- 负载代码必须是真正的小和适合512字节。这可以通过int 0x13 BIOS call来解决。
- 你必须做很多的启动自己的,如移动进入保护模式
正是出于这个GRUB创建了一个名为多重一种更方便的文件格式的原因。
如果您准备OS作为一个多重文件,GRUB是再能找到它一个常规的文件中。
这是大多数发行版所做的,将操作系统映像放在/boot
之下。
多引导文件基本上是一个带有特殊标题的ELF文件。它们由GRUB指定:https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
您可以使用grub-mkrescue
将多引导文件转换为可启动磁盘。
萨尔瓦多Torito的
格式,可以将其刻录成光盘:https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29
它也有可能产生在任ISO或USB工作的混合图像。这可以使用grub-mkrescue
(example)完成,也可以通过使用isohybrid
的make isoimage
上的Linux内核完成。
ARM
ARM的土地有它自己的约定(或多个缺少的约定,因为ARM授权IP到修改它的供应商),但总的想法是一样的:
- 你写代码到一个神奇的地址在内存
- 你做的IO附魔地址
一些差异含UDE:
- IO是通过写地址魔直接做,没有
in
和out
说明 - 你需要添加由供应商提供给您的图像魔术编译封闭源代码的斑点。这些有点像BIOS,这是一件好事,因为它使更新固件更加透明。
下面是一个例子:How to run a C program with no OS on the Raspberry Pi?
资源
http://wiki.osdev.org是一个伟大的源对于这些事项。
https://electronics.stackexchange.com/questions/223929/c-standard-libraries-on-bare-metal/223931为了构建更有趣的软件“裸机软件”,可以方便地使用C的stadard库。一种可能性是使用上述线程中提到的https://en.wikipedia.org/wiki/Newlib。这样的裸机C库实现可以例如将stdout重定向到UART。我认为你必须执行系统调用存根自己的QEMU但是:https://balau82.wordpress.com/2010/12/16/using-newlib-in-arm-bare-metal-programs/
https://github.com/scanlime/metalkit是一个更加自动化/普通裸金属编译系统,提供了一个微小的定制API
https://github.com/dwelch67/raspberrypi看起来像一个顶尖的源Raspbery Pi(ARM)
- 1. 可以在没有操作系统的情况下运行java程序吗?
- 2. 如何操作系统...运行...没有一个操作系统运行?
- 3. 如何在没有JIT的情况下运行程序?
- 4. 如何在没有WIN32 API的情况下执行此操作?
- 5. 如何在没有eval的情况下执行此操作()
- 6. 如何在没有odex的情况下构建CM系统应用程序?
- 7. 如何在没有事务的情况下运行更新/删除操作jpa
- 8. 在没有Android操作系统支持.ts文件的情况下播放HLS
- 9. 如何在没有root权限的情况下运行android系统应用程序?
- 10. 让Java程序在没有标准的情况下运行
- 11. 不能在没有调试的情况下运行VC++程序
- 12. 在没有Java的情况下运行小程序
- 13. 在没有IDE的情况下运行Qt程序
- 14. 在没有在Android中打开应用程序的情况下执行操作
- 15. AirPrint如何在没有驱动程序的情况下工作?
- 16. 在没有嵌入的情况下在Flash应用程序中使用操作系统字体
- 17. 如何在没有shell的情况下执行.py程序?
- 18. 如何在没有输入“python”的情况下在shell中运行python程序
- 19. 如何在没有图形API的情况下操作图形?
- 20. 我的GAE应用程序如何在没有app.yaml的情况下运行?
- 21. 如何在没有Eclipse的情况下运行我的java程序?
- 22. 如何在没有QTCreator的情况下运行我的应用程序?
- 23. 如何在没有Eclipse插件的情况下运行android应用程序?
- 24. 如何在没有包含的情况下运行引导程序?
- 25. 如何编译C程序以在没有命令框的情况下运行?
- 26. 如何在没有settings.py的情况下运行django应用程序
- 27. 如何在没有故事板的情况下运行应用程序?
- 28. 如何在没有调试的情况下从VC++运行应用程序?
- 29. Android库存应用程序如何在没有OutOfMemory的情况下运行?
- 30. 如何在没有开发的情况下运行rails应用程序?rb
在哪个体系结构上? 86?臂? – Kissiel
我一直在说,但很可能是x86或x64 – user2320609
是的,这正是处理器启动的方式。并不一定要进行汇编,C通常用于一些引导程序,也许还有其他一些支持。 –