2014-02-26 120 views
25

如何在没有操作系统的情况下自行运行程序?如何在没有操作系统的情况下运行程序?

您可以创建计算机可以在启动时加载和运行的汇编程序,例如:从闪存驱动器启动计算机并运行驱动器上的程序?

+2

在哪个体系结构上? 86?臂? – Kissiel

+0

我一直在说,但很可能是x86或x64 – user2320609

+0

是的,这正是处理器启动的方式。并不一定要进行汇编,C通常用于一些引导程序,也许还有其他一些支持。 –

回答

12

如何在没有运行操作系统的情况下自行运行程序?

将二进制代码放置到处理器重新引导后寻找的位置(例如,ARM上的地址0)。

您可以创建计算机可以在启动时加载和运行的汇编程序(例如,从闪存驱动器启动计算机并运行驱动器上的程序)?

问题的一般答案:可以做到。 它通常被认为是“裸机编程”。 要从闪存驱动器中读取数据,您想知道USB是什么,并且您希望有一些驱动程序可以使用此USB。此驱动器上的程序也必须采用某种特定格式。在某些特定的文件系统上...这通常是引导加载程序所做的事情。 许多ARM板让你做这些事情。有些引导加载程序可以帮助您进行基本设置。

Here您可能会发现有关如何在Raspberry PI上执行基本操作系统的绝佳教程。

编辑: 这篇文章,整个wiki.osdev.org将ANWER大多数问题的 http://wiki.osdev.org/Introduction

另外,如果你不想直接在硬件上做实验,你可以运行它作为一个虚拟机使用像qemu这样的hypervisor。了解如何直接在虚拟化ARM硬件here上运行“hello world”。

49

可运行实例

技术上,如果没有一个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
  • UEFI。 BIOS后继者,更好地标准化。
  • VGA:特殊内存区域,如果写入,则会打印到屏幕上。可以在保护模式下使用。
  • 写一个驱动程序。这是做到这一点的“正确”方式:更强大,但更复杂。

这里我们来做一个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,-hdamain.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创建了一个名为多重一种更方便的文件格式的原因。

最小工作例如:https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

如果您准备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-mkrescueexample)完成,也可以通过使用isohybridmake isoimage上的Linux内核完成。

ARM

ARM的土地有它自己的约定(或多个缺少的约定,因为ARM授权IP到修改它的供应商),但总的想法是一样的:

  • 你写代码到一个神奇的地址在内存
  • 你做的IO附魔地址

一些差异含UDE:

  • IO是通过写地址魔直接做,没有inout说明
  • 你需要添加由供应商提供给您的图像魔术编译封闭源代码的斑点。这些有点像BIOS,这是一件好事,因为它使更新固件更加透明。

下面是一个例子:How to run a C program with no OS on the Raspberry Pi?

资源

+6

现象的答案! – dramzy

+4

Wtf?为什么这不被接受?神圣...... – Daidon

+0

@Daidon http://meta.stackoverflow.com/questions/326095/please-unpin-the-accepted-answer-from-the-top有一天,一天:-) –

相关问题