GreyZhang/g_unix: some basic learning about unix operating system. (github.com)
JOS的这部分boot相关的代码已经看了一阵子了,中间遇到了很多概念性障碍因此又增补了一些其他常识性知识的学习。现在,整理一下整个boot启动的过程。
首先是进行关中断,然后把各个段寄存器进行一下清零初始化。这里清零得借助一个数据寄存器,而数据寄存器本身的清零则是通过亦或来实现。
启动刚开始的过程,CPU的运行是在16bit的模式下运行的。从上面的分段信息以及相关的注释也是可以看出来的。
接下来,通过使能地址线的操作来实现寻址模式的切换。这期间,进行了一些外设的处理,也进行了从实模式向保护模式的切换。
进入到32bit的保护模式之下,设置了各个段寄存器之后,接下来就是向C语言接口的跳转了。这里用到的跳转接口是bootmain,而C语言中的main也应该是类似的机制。尤其是在嵌入式中,这个机制是一致的。
这样,整个bootmain的大概作用:先从文件系统的最开始读取数据,看看存储在这里的文件是否是ELF格式。如果不是,没法启动。如果是ELF格式,那么根据ELF中提供的信息排判断程序header的存储信息,并进行程序存储镜像的加载。如果不启动,会停在一个死循环上。然而这里的这个硬件操作是什么概念,暂且没有找到合理的解释。如果ELF以及程序header的一切信息加载顺利,会根据ELF header中指定的entry进入到OS服务。
读取程序数据段是通过读取硬盘的sector来实现的,读取的过程采用了比较简单的逐个sector读取的方式。效率可能不是很高,但是也可以说明OS的启动过程了。
合理用到的一个按照字节对齐的round down的算法,理解起来其实是很简单的。这里正好是一个取巧的做法,用了位操作。那么,是不是所有大小的SECSIZE都可以满足这样的算法呢?显然不是!通过最后的按位与操作,其实很容易分析的出来。只有SECSIZE所有的倍数最后几位都是SECSIZE的二进制状态的时候才能够使用这样的方法。至于sector本身的读取操作,太过于底层不去深究了。
关于这里bad的作用,其实可以通过上面的这种强制跳转来测试一下。但是,从测试结果看,至少是跟终端输出是没有什么太大关系的。如下是修改后的运行效果:
并看不到什么具体的信息。但是从指令信息看,应该是进行了硬件操作。由于这边是一个死循环的信息,我猜测大概率可能是一个定时器或者中断之类的禁用操作。后续的学习中,或许能够找到更多的信息来说明下,非常遗憾的是从目前的网络搜索来说暂时还没找到什么合理的说明。