在Linux 0.11中,我们可以看到有一个main.c和一个main()
根据我的理解,目标代码需要一个操作系统来运行它。
我的意思是,既然Linux 0.11是一个操作系统,那么谁在它前面运行它呢? DOS?
答案1
选择的名称main()
只是出于熟悉和美观的原因;没有 C 运行时调用它,就像main()
从用户态程序调用它一样。甚至有一条评论也说明了这一点init/main.c
:
void main(void) /* This really IS void, no error here. */
该main()
函数是从以下位置调用的boot/head.s
:
after_page_tables:
pushl $0 # These are the parameters to main :-)
pushl $0
pushl $0
pushl $L6 # return address for main, if it decides to.
pushl $_main
jmp setup_paging
L6:
jmp L6 # main should never return here, but
# just in case, we know what happens
请注意如何将 的地址main
压入堆栈,并setup_paging
使用 调用jmp
,而不是使用call
,这意味着ret
位于其末尾的 将从 的开头继续main()
。
答案2
Linux内核,特别是0.11时代,都是由硬件BIOS直接加载的。
基本上,BIOS 会查看(软盘的)引导扇区或硬盘的主引导记录,并加载该扇区。对于硬盘,MBR 然后加载“主分区”引导扇区。
这个加载的引导扇区有足够的信息来了解如何将内核加载到内存中,然后运行它。
对于旧的 0.11 磁盘,它实际上是一种软盘引导解决方案,内核位于一个磁盘上,根目录位于另一个磁盘上,因此引导系统非常非常简单。
当Linux处理硬盘时,启动过程仍然非常简单。它是如此简单,以至于可以创建诸如“loadlin”之类的工具,它是一个简单的 DOS 程序,可以加载 Linux 内核并启动到其中,模拟 BIOS 加载程序。这样就可以创建一个 DOS config.sys 菜单来启动 DOS 或 Linux;双引导的早期形式。
但从本质上讲,Linux 内核是从“裸机”加载并接管机器的。
答案3
函数main
是C语言的一个特性。它究竟如何转换为计算机指令供CPU“从这里开始”基本上是编译器实现细节。在裸机上,您通常可以简单地依赖硬件在首次启动时在特定内存地址开始执行。 Linux 的早期版本依赖于 a.简单的 x86 引导加载程序;如今,该角色通常由 Grub 承担。反过来,这种行为取决于 BIOS 固件约定,但实际上,在每个级别,您都有一个计算机体系结构,其中包含如何启动程序的约定。