一步步编写操作系统 51 加载内核4

咱们的内容都是连栽的,如果您没看过我之前的文章,本节您是看不懂的。

接上节。

介绍完内核初始化的函数kernel_init后,本节代码部分还差一点点没说啦,下面看代码:

…略
179 ;在开启分页后,用gdt新的地址重新加载
180 lgdt [gdt_ptr] ; 重新加载
181
182 ;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;;
183 ;由于一直处在32位下,原则上不需要强制刷新,;经过实际测试没有以下这两句也没问题.
184 ;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题.
185 jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt
186 enter_kernel:
187 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
188 call kernel_init
189 mov esp, 0xc009f000
190 jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok
…略

在代码的开头是咱们之前已经完成的重新加载GDT,就是将原来GDT的基地址0x900变成0xc0000900后重新加载。按理说,当前已经是32位环境啦,而且内核也是32位程序,不需要“显式”地清空流水线。实话实说,之前loader.S中是没有第185~186行,而且经我简单测试后其运行结果也是正确的。不过,在调试过程中有可能会碰到稀奇古怪的问题,当然这绝对是人为的错误,不要轻易怀疑计算机。对于内核中这类“灵异”事件,咱们当然希望少碰到,某些bug真的会让人调试好多天,所以为了保险起见,还是用无条件跳转指令刷新了流水线,请大家知晓。

在进入内核之后,我们用的栈也要重新规划了,栈起始地址不能再用之前的0xc0000900啦。为了方便编写程序,我们在进入内核前将栈指针改成我们期待的值,在第189行,我们将esp改成了0xc009f000。此地址的选择也是根据之前的内存布局图。也许有同学会说,为什么不把esp选为0x9fc00,这才是最合理的。没错,您说的对,我们都是会过日子的人,0x9fc00确实是最省空间的选择,这样做,以后的程序也不会出错。但这牵扯到以后要说的pcb,即程序控制块(咱们在以后线程相关章节会细说pcb,这里仅要求大家对此有个浅表的了解即可),每个pcb都是自然页,也就是要求4KB对齐,即4KB的范围是0x000~0xfff,而不是类似0x333~0x1332这样的范围。我们打算在4KB内的最高地址做为栈底,如果以0x9fc00做为栈底,虽然不出会什么问题,但它显得太个性了,比其它pcb少了0x400字节。所以,为了统一pcb大小,我们这里选择栈底的要求是:它接近最大可用地址0x9fbff,并且以4KB对齐,所以0x9f000是最合适的。

为了打消部分同学的疑虑,容我再多说两句。我担心有同学可能会这样想,咱们loader加载的物理地址是0x900,loader中使用的栈的栈底是0x900,栈是往下发展的,在loader以后的压栈操作中,并不会破坏掉loader自身,似乎这种“完美”的方案可以在咱们的kernel中延续,也就是为何不让kernel的栈为入口地址之下?比如咱们这里的入口地址是0xc0001500,也让栈底esp为该值有何不妥。不管怎么说,如果有这种想法,说明您是个爱动脑的同学,我会为您悄悄点赞。其实loader.bin是纯二进制文件,而kernel是elf格式的二进制文件,这两者的区别是elf比纯二进制文件多了文件头,纯二进制文件相当于elf文件中的所有段(segment)的集合。在前面我们分析过啦,程序的入口地址是很可能会在段中的,并不是在段的起始,就拿咱们的kernel.bin来说,它代码段的入口地址是0xc0001500,起始地址却是0xc0001000,入口并不在段的开头。其实0xc0001000~0xc0001500之间的部分是文件头,并不是真正的代码。真要把esp赋值为0xc0001500,如您所料,将来内核中有压栈操作时一定会破坏0xc0001000~0xc0001500之间的部分,虽然它只是文件头不是实际代码,似乎破坏了也无关紧要,但这样做确实不美啊。您看,咱们的内核预计70KB左右,起始物理地址是0x1500,而栈底若为0x9f000,这0x9f000-0x1500约为630KB的空间,在正常情况下,栈是不会碰撞到内核的,这样多省心。所以,兄弟们,我看还是算了吧,咱就选个高地址0x9f000,就它了。

啰嗦过后,loader通过第190行的跳转指令进入内核。这里所见的KERNEL_ENTRY_POINT是boot/include/boot.inc中定义的宏,其值为0xc0001500,它正是我们用ld命令链接kernel时指定的代码段地址,这个宏必须要与其一致才行。

经过这样一番的规划后,现在0x500~0x9fbff可用内存中,咱们自己的文件布局如图:

和内核相关的内容咱们暂告一段落,在本章的结束,咱们说说保护模式下最闪亮的内容——特权

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439668.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Coursera自动驾驶课程第3讲:Self-Driving Hardware and Software Architectures

在上一讲《Coursera自动驾驶课程第2讲:The Requirements for Autonomy》中我们了解到了如何划分自动驾驶汽车等级、以及自动驾驶三大模块:感知、决策和执行。 本讲我们将学习新的模块:自动驾驶汽车的硬件和软件架构。 B站视频链接&#xff…

一步步编写操作系统 54 CPL和DPL入门1

我们在工作中,公司都给员工配有员工卡,此员工卡就是员工身份的“标签”,用它来出入公司、食堂就餐等。给公司创造价值的是员工的生产力,不是员工卡,员工卡只是公司人事部门管理员工的一种手段而已。 现在说计算机&…

Coursera自动驾驶课程第4讲:Safety Assurance for Autonomous Vehicles

在上一讲《Coursera自动驾驶课程第3讲:Self-Driving Hardware and Software Architectures》中我们了解了自动驾驶汽车常用的传感器和硬件组件、软件系统。 本讲我们将学习新的模块,也是自动驾驶汽车最重要的模块之一:安全模块。 B站视频链…

【Codeforces - 1000C】Covered Points Count(思维,离散化,差分)

题干: You are given nn segments on a coordinate line; each endpoint of every segment has integer coordinates. Some segments can degenerate to points. Segments can intersect with each other, be nested in each other or even coincide. Your task i…

Coursera自动驾驶课程第5讲:Vehicle Dynamic Modeling

在上一讲《Coursera自动驾驶课程第4讲:Safety Assurance for Autonomous Vehicles》中我们了解了自动驾驶汽车中一个非常重要的模块:安全模块。 本讲我们将学习新的模块:汽车运动学和动力学模块。(这部分可能需要一定的理论力学和…

Java同步锁——lock与synchronized 的区别【转】

在网上看来很多关于同步锁的博文,记录下来方便以后阅读 一、Lock和synchronized有以下几点不同: 1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,synchronized是在JVM层面…

Coursera自动驾驶课程第6讲:Vehicle Longitudinal Control

在上一讲《Coursera自动驾驶课程第5讲:Vehicle Dynamic Modeling》中我们了解了汽车运动学和动力学模块。 本讲我们继续学习新的模块:汽车纵向控制。具体地,我们将学习PID控制算法,看看该算法是如何在自动驾驶汽车中应用的。 B站…

Java并发:线程共享变量可见性原理

0、线程安全性:线程安全性包括两个方面,①可见性。②原子性。 0.1、线程之间的通信:线程的通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种共享内存和消息传递。 (1)在共…

Coursera自动驾驶课程第7讲:Vehicle Lateral Control

在上一讲《Coursera自动驾驶课程第6讲:Vehicle Longitudinal Control》中我们了解了如何使用PID算法进行汽车纵向控制。 本讲我们继续学习新的模块:汽车横向控制。具体地,我们将学习三种控制算法:Pure pursuit,Stanle…

Coursera自动驾驶课程第8讲:Basics of 3D Computer Vision

在上一讲《Coursera自动驾驶课程第7讲:Vehicle Lateral Control》中我们了解了如何对汽车进行横向控制。 本课程第一个篇章就暂时告一段落了,接下来我们开始学习新的篇章。 课程第二个篇章是状态估计和定位模块。不过在这里我做了一下调整,我…

Coursera自动驾驶课程第9讲:Visual Features Detection Description and Matching

在上一讲《Coursera自动驾驶课程第8讲:Basics of 3D Computer Vision》中我们学习了计算机视觉基本知识。 本讲我们将学习计算机视觉中的视觉特征模块。 B站视频链接:https://www.bilibili.com/video/BV1PE411D72p 文章目录1. Introduction to Image f…

Coursera自动驾驶课程第10讲:Feedforward Neural Networks

在上一讲《Coursera自动驾驶课程第9讲:Visual Features Detection Description and Matching》中我们学习了如何进行图像特征检测,特征匹配以及如何构建视觉里程计来估计相机的运动。 本讲我们将学习神经网络模块,关于神经网络或深度学习网上…

守护进程和守护线程

对于JAVA而言,一般一个应用程序只有一个进程——JVM。除非在代码里面另外派生或者开启了新进程。 而线程,当然是由进程开启的。当开启该线程的进程离开时,线程也就不复存在了。 所以,对于JAVA而言,线程是完全可以由自…

Coursera自动驾驶课程第11讲:2D Object Detection

在上一讲《Coursera自动驾驶课程第10讲:Feedforward Neural Networks》中我们学习了神经网络的基础知识,包括损失函数,梯度下降,正则化,卷积网络等。 本讲我们将学习深度学习的一个重要应用:图像目标检测。…

Coursera自动驾驶课程第12讲:Semantic Segmentation

在上一讲《Coursera自动驾驶课程第11讲:2D Object Detection》我们学习了深度学习的一个重要应用:目标检测。 本讲我们将学习深度学习的另一个重要应用:语义分割。这是图片像素级的一个重要应用。 B站视频链接:https://www.bili…

多线程知识梳理(2) - 并发编程的艺术笔记

layout: post title: 《Java并发编程的艺术》笔记 categories: Java excerpt: The Art of Java Concurrency Programming. <img src"http://upload-images.jianshu.io/upload_images/658453-a94405da52987372.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240…

Coursera自动驾驶课程第13讲:Least Squares

在上一讲《Coursera自动驾驶课程第12讲&#xff1a;Semantic Segmentation》我们学习了深度学习的另一个重要应用&#xff1a;语义分割。至此&#xff0c;本课程的视觉感知模块就介绍完了。 从本讲开始&#xff0c;我们将学习一个新的模块&#xff0c;也是本课程的第三个模块&…

Coursera自动驾驶课程第14讲:Linear and Nonlinear Kalman Filters

在上一讲《Coursera自动驾驶课程第13讲&#xff1a;Least Squares》我们学习了最小二乘法相关知识。 本讲我们将学习20世纪最著名的一个算法&#xff1a;卡尔曼滤波。具体包括线性卡尔曼滤波&#xff08;KF&#xff09;&#xff0c;扩展卡尔曼滤波(EKF)&#xff0c;误差状态卡…

详解两阶段3D目标检测网络 Voxel R-CNN:Towards High Performance Voxel-based 3D Object Detection

本文介绍一篇两阶段的3D目标检测网络&#xff1a;Voxel R-CNN&#xff0c;论文已收录于AAAI 2021。 这里重点是理解本文提出的 Voxel RoI pooling。 论文链接为&#xff1a;https://arxiv.org/pdf/2012.15712.pdf 项目链接为&#xff1a;https://github.com/djiajunustc/Voxe…

java容器类1:Collection,List,ArrayList,LinkedList深入解读

1、 Iterable 与 Iterator Iterable 是个接口&#xff0c;实现此接口使集合对象可以通过迭代器遍历自身元素. public interface Iterable<T> 修饰符和返回值方法名描述Iterator<T>iterator()返回一个内部元素为T类型的迭代器default voidforEach(Consumer<?…