一步步编写操作系统 50 加载内核3

接上节,在这里,我们把参数放到了栈中保存,大家注意到了,参数入栈的顺序是先从最右边的开始,最后压入的参数最左边的,其实这是某种约定,要不,为什么不先把中间的参数src入栈呢。既然主调函数按照从右到左的顺序在栈中压入参数,被调函数中必须分清楚这三个参数分别在栈中哪个位置。栈是向下扩展的,这一点通过push指令压栈时,栈指针esp的值越来越小能体现出来,所以最后压入的第1个参数是离栈顶(esp指向的地址)最近,最先入栈的第3个参数离栈顶最远。我们来看下在参数入栈后并调用函数时,栈中布局是什么,还是拿call mem_cpy为例。如图

由于栈指针esp已经在loader.S中被加上了0xc0000000,所以其栈中地址都是内核所在的0xc0000000以上的高地址。用call指令进行函数调用时,cpu会自动在栈中压入返回地址,由图可见,当调用kernel_init函数时,当时的栈指针是0xc00008fc,所以kernel_init的返回地址被存储在0xc00008fc处。栈中地址0xc00008f8处的内容是提供给函数mem_cpy的第三个参数,即size。地址较低的0xc00008f4处是它的第二个参数,即src地址,0xc00008f0处是它的第一个参数,即dst。

在mem_cpy的实现中,我们访问栈中的参数是基于ebp来访问的,这通常意味着要将esp的值赋给ebp。由于不知道ebp中的值是不是重要,好的习惯是提前将ebp备份起来,这就是在第228行的目的,将ebp入栈备份,这样在函数结束时能够将其恢复。我们在第229行将esp赋值给了ebp。所以上图中,标出了ebp的指向,由于后来在第230行又将ecx入栈,故esp已经小于ebp。

栈中每个单元占用4字节,既然是基于ebp来获得栈中的参数,那么如图所示,第1个参数dst的地址是ebp+8,第2个参数src的地址是ebp+12,第3个参数size的地址是ebp+16。分别对这些地址用中括号取值后,便可以得到实际的参数。

在继续往下说之前,要给大家介绍个数据复制小团队。

首先要说一下字符串“搬运”指令族:movsb、movsw、movsd。其中的movs代表move string,后面的b代表byte,w代表word,d代表dword。所以movsb的功能是搬运(复制)1字节,movsw的功能是搬运(复制)2字节,movsd的功能是搬运(复制)4字节。数据从哪里来,搬到哪里去呢?这三条指令是将DS:[E]SI指向的地址处的1或2或4个字节搬到ES:[E]DI指向的地址处,16位环境下源地址指针用SI寄存器,目的地址指针用DI寄存器,32位环境下源地址则用ESI,目的地址则用EDI。话说虽然这三个指令叫字符串指令,但它们可不是只用在字符串上,因为字符串中的字符不也是按字节来存储吗,任何数据在内存中都以字节存储单元来访问,字符串只是表相,本质上是复制字节,所以它更多的被通用于复制数据。

以上三个命令只是复制固定的字节数,每执行一次就复制1字节或2字节或4字节,如果大量的数据需要复制,则需要连续的运行,所以要介绍另外一个指令rep。

rep指令是repeat重复的意思,该指令是按照ecx寄存器中指定的次数重复执行后面的指定的指令,每执行一次,ecx自减1,直到ecx等于0时为止,所以在用rep重复执行某个指令之前,一定要将ecx寄存器提前赋值。

似乎说完了,但其实还差点什么,您想,如果想要复制一大块数据的话,总该有人更新数据的来源和目的地吧。movs [bwd]只是从[e]si指向的地址处搬运1、2、4字节到[e]di指向的地址处,它不会自动更新[e]si和[e]di。咱们总不能翻来覆去从同一个源地址搬运数据到另一个相同的目的地址吧。所以,cld和sld指令就派上用场了,这两个指令本质上是控制重复执行字符串指令时的[e]si 和[e]di的递增方式,递增方式是指它们的值逐渐变大还是逐渐变小,也就是说,地址是往高地址方向变化,还是往低地址方向变化,这就是所说的方向。cld是指clean direction,该指令是将eflags寄存器中的方向标志位DF置为0,这样rep在循环执行后面的字符串指令时,[e]si和[e]di根据使用的字符串搬运指令,自动加上所搬运数据的字节大小,这是由cpu自动完成的,不用人工干预。比如,执行一次movsd,[e]si和[e]di就自动加4,执行一次movsb,[e]si和[e]di就自动加1。有清除方向标志位就会有设置方向标志位,std是set direction,该指令是将方向标志位DF置为1,每次rep循环执行后面字符串指令时,[e]si和[e]di自动减去所搬运数据的字节大小。

也许cpu认为地址由低向高处发展是理所应当的,这无须设置,所以此时DF标志为0。当由高地址向低地址发展时,这不是正常自然的现象,所以需要强调一下,故要将DF标志置为1。

注意,并不是在任何字符串控制指令中[e]si和[e]di都同时增减,这要看字符串操作指令是否都用到了它们,处理器只会增加用到的那个。字符串操作指令有很多,比如有movs[bwd]、ins[bwd]和outs[bwd]、lods[bwd]和stos[bwd],esi和edi并不是被以上三组指令同时使用,只有movs[bwd]才同时使用esi和edi,通过rep指令组合执行时,esi和edi根据DF位的值自增或自减。ins[bwd]是从端口读入数据到内存的目的地址,故只涉及到edi的自增自减。outs[bwd]是把内存中的源数据写入端口,故只涉及到esi的自增自减。lods[bwd]是把内存中的源数据加载到寄存器al、ax或eax,自增自减操作也只涉及到esi。而stos[bwd]是将al、ax、eax中的值写入到内存中的目的地址,故也只涉及到edi的自增自减。

好啦,在稍微扩展了一小下之后,咱们回到正题。

有了movs[bdw]指令族、重复执行指令rep,方向指令cld和std,这三剑客在一起配合工作就能够自由复制任何大块数据啦。万事俱备,回到正题。

第227行的cld指令其实放在movsb之前就行,它是用于清除方向标志,让数据的源地址和目的地址逐渐增大。

由于外层函数也要用ecx做为遍历段的循环计数,所以您明白了,这里的第230行为什么要将ecx入栈备份啦,这样在ecx用完之后,在mem_cpy执行结束前通过pop指令将ecx和ebp恢复,以便外层遍历段的循环中保持ecx正确。

在第231~233行,为复制工作所需要的条件初始化,esi和edi指向了要复制的段的来源地址和目的地址,ecx是为rep指令做准备的,指定了调用movsb指令的次数。在此提醒一下,段寄存器DS和ES在进入保护模式之初就被赋成相同的选择子了,它们都指向同一个段描述符,故它们在此工作正确,请大伙儿放心。

一切就绪之后,在第234行,rep movsb,这三剑客团队就开始合作啦。

mem_cpy返回后,程序流程回到第216行,这是清理在调用mem_cpy之前在栈中压入的size,src,dst,这三个参数共占3*4=12字节,所以将esp加上12,于是栈顶跨过了它们,这三个参数所占的空间可被其它压栈操作覆盖。

每个函数中都要有个返回指令,这里用的是ret指令,以后我们还会接触到其它返回指令。之前在用call指令调用函数时,无论是调用kernel_init还是mem_cpy,cpu都会将函数的返回地址压入栈中保存,这是为函数体中的ret指令准备的,换句话说函数不会自己返回,是通过ret来返回的。ret指令将栈顶中的值做为返回地址,所以,一定要确保在调用ret时,位于栈顶处的数据是正确的返回地址。一般情况下,我们在函数体中保证push操作和pop操作配套成对,正如在mem_cpy的实现中,有两个push入栈操作,在函数返回前就要有两个pop出栈操作。

咱们的函数中用的都是ret近返回指令,所以只会在栈顶弹出4字节的数据做为代码段的偏移地址为EIP寄存器赋值,从而恢复了程序执行流.

【再续】

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

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

相关文章

动手学无人驾驶(5):多传感器数据融合

本系列的前4篇文章主要介绍了深度学习技术在无人驾驶环境感知中的应用,包括交通标志识别,图像与点云3D目标检测。关于环境感知部分的介绍本系列暂且告一段落,后续如有需要再进行补充。 现在我们开启新的篇章,在本文中将会介绍无人…

详解两阶段3D目标检测网络PVRCNN:Point-Voxel Feature Set Abstraction for 3D Object Detection

在《动手学无人驾驶(4):基于激光雷达点云数据3D目标检测》一文中介绍了3D目标检测网络PointRCNN。今天介绍该作者新提出的3D检测模型:PVRCNN,论文已收录于CVPR2020。 作者个人主页为:https://sshaoshuai.gi…

一步步编写操作系统 53 任务状态段TSS介绍

操作系统是利用PCB来维护所有任务的,包括进程和线程,但cpu提供的是TSS,linux系统可没用它,因为效率太低。但是还是要了解下TSS才清楚操作系统中某些操作的原因。 本节中所讲的特权级与它有着密不可分的联系,TSS作用不…

动手学无人驾驶(6):基于IMU和GPS数据融合的自车定位

在上一篇博文《动手学无人驾驶(5):多传感器数据融合》介绍了如何使用Radar和LiDAR数据对自行车进行追踪,这是对汽车外界运动物体进行定位。 对于自动驾驶的汽车来说,有时也需要对自身进行更精确的定位,今天…

【Codeforces - 798C】 Mike and gcd problem(思维,贪心)

题干: Mike has a sequence A  [a1, a2, ..., an] of length n. He considers the sequence B  [b1, b2, ..., bn] beautiful if the gcd of all its elements is bigger than 1, i.e. . Mike wants to change his sequence in order to make it beauti…

一步步编写操作系统 48 加载内核1

其实,我们等了这一刻好久好久,即使我不说,大家也有这样的认识,linux内核是用c 语言写的,咱们肯定也要用c语言。其实...说点伤感情的话,今后的工作只是大部分(99%)都要用c语言来写&am…

Coursera自动驾驶课程第1讲:Welcome to the self-driving cars specialization

本专栏为Coursera自动驾驶课程笔记,B站上也有配套课程视频,对自动驾驶技术感兴趣的朋友可以看看。 本讲对应视频: https://www.bilibili.com/video/BV1WE411D74g?p1https://www.bilibili.com/video/BV1WE411D74g?p2https://www.bilibili.…

Coursera自动驾驶课程第2讲:The Requirements for Autonomy

上一讲《Coursera自动驾驶课程第1讲:Welcome to the self-driving cars specialization》对本课程进行了整体概述,同时回顾了自动驾驶汽车的发展历史。 从本讲我们开始进入正式的学习,我们将首先了解到如何划分自动驾驶汽车等级、以及自动驾…

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

咱们的内容都是连栽的,如果您没看过我之前的文章,本节您是看不懂的。 接上节。 介绍完内核初始化的函数kernel_init后,本节代码部分还差一点点没说啦,下面看代码: …略 179 ;在开启分页后,用gdt新的地址…

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…