• Linux0.11——操作系统怎么把自己从硬盘搬到内存


    这里先直接给出答案:中断

    在这里插入图片描述
    此时,操作系统用短短几行代码,将数据段寄存器ds和代码段寄存器cs设置为了0x9000,方便之后的程序访问代码和数据,并且将栈顶地ss:sp设置在了远离代码的位置0x9000足够遥远的0x9FF00,保证了栈向下发展时不会轻易覆盖掉操作系统的代码。

    简单来说,就是设置了如何访问数据段、如何访问代码段以及如何访问栈段,也就是做了一次初步的内存规划。因为从CPU的角度出发,访问内存就是这么三个地方而已。

    在这里插入图片描述

    做好这些基础工作之后,我们现在仅仅是把硬盘中最开始的512字节加载到内存中了,但操作系统还有很多代码仍然在硬盘中的其他扇区里,不能抛下它们不管啊🤣

    把剩下的操作系统的代码从硬盘请到内存中来

    所以下一步,自然是把仍然在硬盘中的操作系统代码请到内存中来。

    接着往下看代码:

    load_setup:
        mov dx,#0x0000      ; drive 0, head 0
        mov cx,#0x0002      ; sector 2, track 0
        mov bx,#0x0200      ; address = 512, in 0x9000
        mov ax,#0x0200+4    ; service 2, nr of sectors
        int 0x13            ; read it
        jnc ok_load_setup       ; ok - continue
        mov dx,#0x0000
        mov ax,#0x0000      ; reset the diskette
        int 0x13
        jmp load_setup
    
    ok_load_setup:
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里有两个我们值得关注的点int指令。
    这个int指令在汇编中指的是表示中断指令,不是C语言里的int类型🤣,int 0x13表示发起0x13中断,这条指令的各种mov指令都是用来给dx,cx,bx,ax赋值,这四个寄存器都是作为这个中断程序的参数,这叫通过寄存器来传参。与之相对应的是另一种传参方式是通过栈传递,在C语言中应用很广。

    中断可以简单理解为一种通知机制,用来打断当前CPU正在执行的指令,转而去执行相应的中断处理程序的过程。更详细的原理和分析,可以自行Google。

    发起中断后,CPU就会通过这个中断号0x13,去寻找对应的中断处理程序的入口地址,并跳转过去执行,逻辑上就相当于执行了一个函数。

    而0x13号中断的处理程序,是BIOS提前给我们写好的,具体可以去Google BIOS的中断,0x13表示的是读取磁盘的中断函数,之后真正进入操作系统内核之后,中断处理程序还要我们自己编写,目前先用的是BIOS的中断函数。

    可见即便是操作系统的源码,有时也需要去调用现成的函数方便自己工作,并不是造轮子的人就非得完全从头开始造。

    Linux在用这个0x13中断干了什么呢?我们直接看代码:

    load_setup:
        mov dx,#0x0000      ; drive 0, head 0
        mov cx,#0x0002      ; sector 2, track 0
        mov bx,#0x0200      ; address = 512, in 0x9000
        mov ax,#0x0200+4    ; service 2, nr of sectors
        int 0x13            ; read it
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里的代码注释已经很清楚了, 直接说它的作用——从硬盘的第二个扇区开始,把数据加载到内存0x90200处,共加载4个扇区。图示就是这样的:

    在这里插入图片描述

    load_setup:
        ...
        jnc ok_load_setup       ; ok - continue
        ...
        jmp load_setup
    
    ok_load_setup:
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    再往后的jnc和jmp,表示成功和失败分别跳转到哪个标签处,相当于我们高级语言中的if-else。

    可以看到,如果复制成功,就跳转到 ok_load_setup 这个标签;如果失败,则会不断重复执行这段代码,也就是重试。因此我们先不需要关心重试逻辑了,直接看成功后跳转的 ok_load_setup 这个标签后的代码:

    ok_load_setup:
        ...
        mov ax,#0x1000
        mov es,ax       ; segment of 0x10000
        call read_it
        ...
        jmpi 0,0x9020
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    剩下的核心代码就都写在这里了,就这么几行,其作用是把从硬盘第 6 个扇区开始往后的 240 个扇区,加载到内存 0x10000 处,和之前的从硬盘复制到内存是一个道理。

    在这里插入图片描述
    至此,整个操作系统的全部代码,就已经全部从硬盘加载到内存中了。然后这些代码,又通过一个熟悉的段间跳转指令 jmpi 0,0x9020,跳转到 0x90200 处,就是硬盘第二个扇区开始处的内容。

    那这里的内容是什么呢?就是我们要阅读的第二个操作系统源代码文件 setup.s。

    聊聊操作系统的编译过程不过先不急,进入第二个源代码文件之前,我们先简单复盘一下整个操作系统的编译过程。整个编译过程,就是通过 Makefile 和 build.c 配合完成的,最终达到这样一个效果:1. 把 bootsect.s 编译成 bootsect 放在硬盘的 1 扇区;
    2. 把 setup.s 编译成 setup 放在硬盘的 2~5 扇区;
    3. 把剩下的全部代码(head.s 作为开头,与各种 .c 和其他 .s 等文件一起)编译并链接成 system,放在硬盘的随后 240 个扇区。所以整个路径如下图所示:

    在这里插入图片描述

    总结

    • bootsect.s文件就是把操作系统的代码挪来挪去,最后形成上面的内存结构i,然后做了一次比较重要的内存规划,方便后续代码的执行时,数据段和代码段以及栈段的寻址方式。
  • 相关阅读:
    字符串常量池位于JVM哪里
    基于Python实现的全球新冠病毒数据分析
    小程序虐我千百遍,我还怎么待她如初恋,回忆我五年的小程序开发之路
    YOLOv5 分类模型 Top 1和Top 5 指标实现
    常见问题小结
    设计模式之责任链模式
    简述数据中心网络的特点,数据中心网络规划设计
    基于C++的OpenCV项目实战——零部件的自动光学检测
    最新AI创作系统ChatGPT源码+详细搭建部署教程,支持AI绘画/支持OpenAI-GPT全模型+国内AI全模型
    JavaScript
  • 原文地址:https://blog.csdn.net/qq_43460068/article/details/133321933