系统内核的加载
计算机上电后第一个执行的程序是BIOS,BIOS存放在ROM中 (现在一般放在flash中),BIOS将磁盘前512字节的MBR (主引导记录,记录了磁盘的分区表等信息)读到(或加载到)内存地址为0X7C00的位置,从此位置运行程序。 之后MBR将活动分区(装有操作系统)的第一个扇区的512字节也加载到地址为0X7C00的位置,又从此位置运行程序,进行操作系统的加载。
以下转载于http://blog.csdn.net/wishfly/article/details/6215169
在计算机中,对于运行而言最重要的东西其实不是操作系统,也不是应用程序,而是BIOS或者类似的东西,它里面记录着一张拓扑图, 这张拓扑图描述了计算机的硬件是如何连接在一起的-比如网卡芯片在第几条pci总线上,以及各个硬件是如何配置的-比如时钟的值或者启动顺序, 有了这张图,以后的操作系统才可以在这张图上谱写绚美的华章。在操作系统中,经常要处理与中断相关的信息, 每个设备的中断号从哪里来? 这些都需要bios对信息的提供,计算机的启动程序存放在哪里?这些信息也需要bios的提供。比方说从ide的第一个磁盘启动操作系统的话, 那么系统自然会把执行权力交给ide第一块磁盘中存放的某个程序。为了不同磁盘对不同操作系统的标准化,每个启动磁盘的前512字节被称作MBR。
mbr中存放了启动程序和该磁盘的分区表,分区表一共描述了4个分区,其中一个分区为可启动分区,mbr中的启动程序的任务就是在分区表中 找到这个启动分区,然后将执行权力交给启动分区的引导程序,至于这个引导程序如何做,那就交给操作系统的设计者来完成了,可以直接启动操 作系统,也可以在启动操作系统前完成一些别的工作。以上就是标准化的操作系统自举的过程,然而为何必须这么做呢?因为第一,如果不是通过用 户的设置,计算机不知道系统存储在哪个外部存储设备上,因此有了bios的启动顺序;第二,即使计算机知道了从第一块磁盘启动,由于磁盘可以 有很多分区,它也不知道操作系统在哪个分区,因此就有了磁盘分区表中必须有一个启动分区。
就是因为计算机太傻,有了上述的两个“不知道”,所以如果解决了这两个不知道,那么就不用在计算机内部设计这么多复杂的所谓的“智能化”了, bios已经解决了第一个计算机不知道的问题,而lilo/grub等bootloader解决了第二个不知道的问题,因此自从有了grub之后,启动操作系统也成了 可配置的了,和在bios中配置从特定设备启动计算机一样,在grub中可以配置在哪个磁盘分区启动哪个操作系统,因此grub根本不需要磁盘分区 的active标志,一切全部都由用户的配置搞定,之所以拥有分区的active标志,那纯粹是为了操作系统自举时的自动判断启动分区所在时使用。自从 有了grub,大家可以放弃很多概念了,比如启动分区之类的。
说实话,grub等bootlaoder就是一个mini OS,它的作用就是启动另一个real OS,grub的实现比简单的执行mbr的代码,判断哪个分区 是启动分区,然后跳转到启动分区这种“自动化的配置”要复杂,这个复杂性主要表现在这个bootloader是可以配置的。如此复杂的代码不可能仅仅 占据446字节(512-分区表)的空间,于是grub采用了“跳蛙”战术,从最简单的mbr中的代码加载其后的更大的一点的代码从而可以识别“文件系统”,然后 跳转到具体的“文件系统”中,执行grub命令,加载存在于具体“文件系统”中的内核和内存盘,最终启动linux操作系统(启动windows 等不直接支持grub的操作,需要显式跳转到windows的启动分区来完成启动)。以linux为例,如果启动到了initrd,那么此时肯定已经完成了内核 的加载,可以说一个系统已经完全启动完成了,余下来的只是“根”的挂载问题了,对于liveCD之类的系统,到此步就完成了,因此我们的讨论也仅 到此步。那么此时的linux还会“记得”它是从那块磁盘的哪个分区加载的吗?答案是不!因为grub的kernel和initrd命令将内核和内存盘从具体 的“文件系统”加载到内存,只要完成了此步,内存中就已经有了系统运行的全部信息,此时将所有的磁盘拔下,内核还是会从解压开始到达initrd 的执行的,毕竟数据和代码都在内存中,此时内核仅仅是内存中的内核,它是一个全新的执行体,并不知道grub的任何信息,也不知道是谁将它引导 起来的(grub和linux内核的解耦合),内核将丢失所有grub所知道的硬件信息,当然包括磁盘信息,如想知道此时内存中的内核和initrd是从哪一块 磁盘的哪一个分区被加载到内存的,不得不从sysfs的block目录中得到更详细的信息,然后遍历所有的磁盘,寻找特征从而加以判断。如果我事先 做好了一个dom盘,cf卡或者磁盘,然后事后可能将之插入到不同的机器上,鉴于每台机器的ide口的硬连线可能顺序不同,比如在pc1上的ide口是 hda,在pc2上或许就是hdb,如果我将grub的配置文件写成root=/dev/hda1那么在pc2上可能就无法启动,如何能做到自适应启动呢?在仅有一块块 设备(磁盘)的情况下,我可以在initrd的/init脚本中挂载sysfs在/sys,然后再在/sys/block/下查找唯一的磁盘,然后直接挂载之,然而如果不仅仅 有一块磁盘呢?那么一个可选的方法就是在启动磁盘的/下创建一个文件abc(确保唯一性),然后分别挂载所有的/sys/block下的磁盘,查找下是否 有一个abc的文件…仅此…