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

其实,我们等了这一刻好久好久,即使我不说,大家也有这样的认识,linux内核是用c 语言写的,咱们肯定也要用c语言。其实...说点伤感情的话,今后的工作只是大部分(99%)都要用c语言来写,还有一些要用到汇编的地方。大家也不要因此气馁心灰(其实突然不用汇编还会想它呢,这不是玩笑),我在此过程中一定会尽我所能让内容简单易接受。

我们的内核文件是kernel.bin,这个文件是由loader将其从硬盘上读出并加载到内存中的,到此,接力棒传到了最后一个选手的手里。也就是说,咱们需要事先把kernel.bin定入硬盘。好久不往虚拟硬盘上写东西了,甭说是大家,我都有点陌生了呢,不过好在操作很简单,写之前让我们先看看这块虚拟硬盘上的文件布局吧。

MBR是写在了硬盘的第0扇区,第1扇区是空着的,原因是个人喜好,其实不空着也行,不过硬盘那么大,何必搞得那么拥挤呢。因此loader是写在硬盘的第2扇区,由于loader.bin目前的大小是1342字节,占用3个扇区,所以第2~4扇区不能再用啦,从第5扇区起我们可以自由使用。但此时我的强迫症又发作啦,我这里并没有接着第5扇区写,而是选的第9扇区(要是起始为1的话算是第10个扇区)。一是为了loader万一哪天要扩展,得预留出硬盘空间,二是您可能已经预计到了,隔开点显得更放心,这纯属是出于个人喜好做出的选择。

好,既然已经确定了写入扇区的位置,我们还是要通过dd命令往磁盘上写,命令如下:

dd if= kernel.bin of=/your_path/hd60M.img bs=512 count=200 seek=9 conv=notrunc回车

seek为9,目的是跨过前9个扇区(第0~8个扇区),我们是在第9个扇区写入。

count为200,目的是一次往参数of指定的文件中写入200个扇区。

至于为什么把count设成这么大,原因是这样的:每次写完内核后,咱们要往磁盘中同步内核文件,这样才能验证内核的正确性。按理说,咱们现在的内核文件不足4扇区,count=4最合适。不过,内核发展越来越大时,每次都要根据实际内核文件大小去改写count参数,这样就难免会有忘记修改的情况。之前我就深受其苦,内核文件变大了,而count忘记调整,造成写入硬盘中的内核文件不完整,所以到后来,程序运行不受控制,以至于调试的时候都调晕啦,看着cpu中跑的指令我完全蒙圈了,根本不是自己写的。恍然大悟之后,我就干脆一步到位,因为我们将来的内核大小不会超过100KB,所以直接把count改为200块扇区。另外请大家不用担心,dd命令会自己判断写入的数据量,如果参数if指定的文件体积小于count*bs,只按实际文件大小写入。

不过,估计您也觉得参数太多了,为了方便,我通常是把下面三个命令,编译、链接、再写入硬盘一起完成,您可以将它们写成一个脚本,脚本内容如下:

gcc -c -o main.o main.c && ld main.o -Ttext 0xc0001500 -e main -o kernel.bin && dd if= kernel.bin of=/your_path/hd60M.img bs=512 count=200 seek=9 conv=notrunc

好啦,上面命令在回车之后,这样我们的内核文件就成功写进磁盘了。

菜配好啦,就等下锅啦,我们的内核是由loader加载的,所以我们还要去修改下loader.S。

loader.S需要修改两个地方:

  • λ加载内核:需要把内核文件加载到内存缓冲区。
  • λ初始化内核:需要在分页后,将加载进来的elf内核文件安置到相应的虚拟内存地址,然后跳过去执行,从此loader的工作结束。

先说第一个加载内核,这里所说的加载内核只是把内核从硬盘上拷贝到内存中,并不是运行内核代码。这项工作在开启分页前后都可以,不过为了简单,咱们把它安排在分页开启之前加载。

话说内核加载到内存中,得有个加载地址,也就是缓冲区。其实开发经验少的同学对缓冲区这个概念总是觉得有点“只可意会不可言传”的意思。借此机会多说两句。缓冲区,buffer,意味存放物品的地点,也就是用于加工处理中暂存数据的地方。生活中的缓冲区例子有很多,比如水杯是水的缓冲区,水不是直接入口的,总有个中间载体做为中转,然后才入口。而且,水杯的作用相当于暖瓶或水房的缓存,咱们不是喝一口水就跑到水房接一口水,而是一次接一大杯,回来慢慢喝,这样就减少了去水房的次数。由此可见,缓冲区,既有存放数据的空间之意,又有提高效率的缓存之意。换在计算机世界里,缓冲区必然也是个能存储数据的介质,比如咱们这里所说的内存。

好啦,不能扯太远啦,咱们的缓冲区在设在哪里呢,这不是乱放的,得参考下目前内存中哪个地方还有可用的空间,千万不能覆盖了重要数据。也许大家首先想到的是很久之前说到的那个内存布局图,赞,答对啦,不过,大家不用往前翻看啦,一向体贴的我已经将其重点部分摘到这里啦,大家请看图

内核被加载到内存后,loader还要通过分析其elf结构将其展开到新的位置,所以说,内核在内存中是有两份拷贝,一份是elf格式的原文件kernel.bin,另一份是loader解析elf格式的kernel.bin后在内存中生成的内核映像(也就是将程序中的各种段segment复制到内存后的程序体),这个映像才是真正运行的内核。

将来内核肯定是越来越大,为了多预留出生长空间,咱们要将内核文件kernel.bin加载到地址较高的空间,而内核映像要放置到较低的地址。内核文件经过loader解析后就没用啦,这样内核映像将来往高地址处扩展时,也可以覆盖原来的内核文件kernel.bin。所以咱们的结论是,在0x7e00~0x9fbff这片区域的高地址中找一亩地给kernel.bin,这里我擅自做主啦,帮大家选的是0x70000。为什么?没有为什么,随意选的,取了个整而已,就是觉得0x70000~0x9fbff有0x2fbff=190KB字节的空间,而我们的内核不超过100KB,够用就行。

好,万事俱备啦,代码走起,请大家过目代码

147 ; ------------------------- 加载kernel ----------------------
148 mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号
149 mov ebx, KERNEL_BIN_BASE_ADDR; 从磁盘读出后,写入到ebx指定的地址
150 mov ecx, 200 ; 读入的扇区数
151
152 call rd_disk_m_32
153
154 ; 创建页目录及页表并初始化页内存位图
155 call setup_page

代码属于loader的一部分,它的作用是把内核文件从硬盘上加载到内存中,下面简要说一下。

第148~149行的KERNEL_START_SECTOR和KERNEL_BIN_BASE_ADDR是在boot/include/boot.inc中定义,其值分别为0x9和0x70000。

第150行的ecx为200,这是读入的扇区数,这里应该同前面用dd命令往硬盘上写入内核文件时的参数count保持一致,原因你懂的不解释。

以上的eax、ebx、ecx是函数rd_disk_m_32的三个参数,是为调用下面的函数做准备。

第152行的函数是rd_disk_m_32,用于从硬盘上读取文件。它的三个参数已经在上面赋值了。由于目前已经在32位保护模式下,所以相比之前位于mbr中的函数rd_disk_m_16,rd_disk_m_32只是版本由16位变成了32位的,函数实现原理相差无几,主要体现在里面所用的寄存器变成了32位。所以,就不细说啦,大家一看就明白啦。

接下来的第155行就是开始创建页表啦,把它放在这是为了让大家知道代码是加到了哪里,承上启下。setup_page函数实现没变,无须多说。

内核加载到缓冲区中后,现在该说要修改的第二处啦,也就是初始化内核。

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

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

相关文章

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…

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;也是本课程的第三个模块&…