Nachos 3.4入门的两个问题

win7下ubuntu的硬盘安装之前一篇文章已有介绍,不再详述。在获得nachos的源码包之后,解压到/usr/local目录下,就可以发现在3.4中nachos一共有下面几个主要的文件夹:

1.bin 这里主要放的是交叉编译要用到的一些东西,.noff文件就是可以运行在nachos环境下的用户程序,这里由于第一阶段主要是threads的学习,这个可以先不管。

2.filesys 顾名思义,文件系统。

3.machine 这个文件夹的东西很有意思,是整个nachos最有意思的部分,也就是模拟硬件,其他部分只不过是os原理的具体实现

4.network 网络部分

5.test 这里是一个测试,在halt.c中测试系统的Halt()函数

6.threads         这里是系统线程的实现,下面会介绍关于它的两个很有意思问题

7.userprog       这里放的是要跑在nachos上的用户程序

8.vm                 这里就一个makefile,可以打开看,主要是对一些关键代码的组织编译。

最后就是几个Makefile文件,也是编排编译整个代码结构的。

 

由于3.4版本中,各个模块可以单独的运行,所以我们对于首先关注的threads这个模块,就可以进行单独编译,这里由于编译器版本的问题,可能需要对某些代码文件进行修改,否则会出现很多编译错误,网上有这方面介绍,但是不是很多,而我自己之前也修改的是4.1版本的nachos,目前手里的代码是老师给的,错误已经被修改了,可以直接make,有需要的朋友可以留邮箱。

进入到threads目录下,执行make,就会生成一个nachos的可执行文件,然后点击运行,控制台下就会出现两个线程交互打印的结果,然后输出一些参数,包括总的时钟周期,系统的时钟周期和用户的时钟周期,这里就算是环境基本上搭起来了。

 


 

第一个问题:整个nachos是个怎么样的逻辑?即哪些东西是跑在我们的linux系统上的,哪些东西是跑在nachos的?

下面这个图是nachos咋linux上的层次结构:

 

nachos

我们可以这么理解,nachos本身就是一个c++程序而已,本质上和helloword没啥区别,可能就是代码行多一些,模块复杂一些,但是在linux看来,他们是一样的东西,而我们所要做的也就是修改一个c++程序,不管这个c++程序有多复杂,它还是一个c++程序,并且在threads中尽管它模拟了多线程,但是nachos本身还是一个单线程的程序,弄清这点可以帮我们更好的认识下面的问题。所以说,除了交叉编译之后的.noff文件,其他的文件都是linux上的c++程序,当然为了实现线程交换还使用了汇编,而.noff是第二部分用户程序的内容,这里先不细说。

 


 

第二个问题:我们在threads文件夹下编译的nachos是怎么模拟的多线程?

要解释这个问题,最好也是最简单的办法就是读代码。下面我按顺序贴上这第一个程序的执行流程,这里推荐kscope或者在windows下用sourceInsight 阅读代码。

1.main.cc的main函数

          

         这里我们可以看到在main中首先调用了Initialize,参数不重要,因为事实上第一个程序和参数还没关系,如果使用si,则鼠标选择Initialize上的时候,下面的副代码框就会显示这个函数的实现部分,kscope则需要查找一下,这个函数在system.cc中:

2.system.cc的Initialize函数

 

之前还有很多的设置代码我没有贴出,在这个程序中都不是重点,在这里可以看到,程序新建了一些用于控制的对象,比如中断和调度器,最后还新建了一个名字为main的thread对象,并设置为正在运行,这里不要把它想成一个真的线程,它就是一个自定义的数据结构。那么现在我们可以知道,我们已经有一个假设的线程了,由于构造函数中只是定义一些成员变量,这里我就不再贴了。这个函数到这里就返回了,下面继续到main中去:

3.main.cc的main函数

 

不用细说,直接去找这个函数的定义,在ThreadTest.cc中

4.TheadTest.cc的ThreadThest函数

 

Debug不管,这里又新建了一个Thread对象,也就是说我们已经有了两个Thread对象,一个叫main,一个叫forked thread。

并且给forked thread传了一个函数指针和参数,由于整个程序是一个单线程的顺序执行,所以下面就去看看fork函数看了什么。

5.Thread.cc的Fork函数

 

这里有两个关键的函数,一个是StackAllocate,一个是ReadyToRun,继续顺藤摸瓜

6.Thread.cc的StackAllocate函数

 

可以看到这里主要是对线程的栈进行初始化,AllocBoundedArray完成的就是这个功能,而在最下面,可以看到几个关键的函数地址都被存到了相应的数组中,这个数组就是用来存放模拟寄存器数据的,其中ThreadRoot是第一个要执行的函数,所谓的PC计数器嘛,而ThreadFinish是程序最后要执行的东西,这两个函数是在涉及到汇编,以后有机会再说吧。

7.Scheduler.cc的ReadyToRun函数

 

这里可以看到,我们将forked thread设置为Ready,并添加到了等待队列中了,再次声明:这些都是普通的对象,没有涉及到linux上真正的多线程。到这里fork的过程结束,进入了SimpleThead这个函数。

8.TheadTest.cc的SimpleThread函数

 

关键的地方就是Yield函数,其他的都很好理解,这里肯定已经打印了0线程,并且currentThead指向的叫main的对象,是他调用了Yield函数。

9.Thread.cc的Yield函数

 

这里的关键是scheduler的FindNextToRun函数和Run函数,ReadToRun上面已经介绍,就是将当前Thread对象添加到一个等待队列中。

10.Scheduler.cc的FindNextToRun函数

 

这个,不解释!

11.Scheduler.cc的Run函数

 

这段中的最关键的是最后一行SWITCH,这个函数实现了两个线程的切换,主要是保存的各种函数指针和参数的上CPU和下CPU,这些是我们保存之前对象和运行另一个对象所必须要知道的信息,在这个具体的例子中,就是保存main的信息并调出forked thread的要函数指针和参数,这样在接下来就会打印thread1.而同时又会调用Yield函数,这时候和上面同理,不过又开始执行main这个thread对象的东西,通过这种方式模拟了多线程。所以说SWITCH是精华部分,之后有机会好好研究一下。由于是main先开始running,所以最后也是main这个Thread先跳出这个TheadTest,返回main函数

12.main.cc的main函数

 

这段注释比代码多,一言以蔽之,就是这里不会返回,最后那个return也不会执行,因为nachos是一个单线程的程序,所以先去看看finish中做了些什么。

13.Thread.cc的Finish函数

 

这里就是把当前的currentThread指向的对象赋给了threadToBeDestroyed,然后sleep,并没有任何删除动作。

14.Thread.cc的Sleep函数

 

这里非常有意思,可以多写几个打印语句看下执行流程。第一次我们sleep的时候,main的状态设置为阻塞,然后去等待队列找下一个要执行的线程,很明显,返回的是forked thread,而它这时才从simpleTest真的出来,然后和main一样finish,sleep,然后又到这里,这时候等待队列为空,就会执行interrupt的Idle()函数。

15.Interrupt.cc的Idle函数

 

这里就会打印很多我们在控制台可以看到的信息,然后执行Halt函数

16.Interrupt.cc的Halt函数

 

打印各种参数,即用了多少时钟周期,当然这个周期也是nachos自己定义的,关于这个,以后有机会说。

17.System.cc的CleanUp函数

 

到此,全部的控制台的信息,我们都找到了归宿,这就是整个的流程:一个单线程的nachos模拟了多线程的运行

 

 

最后说一句:Nachos的代码风格非常好,注释也十分详细,十分便于学习os的原理,读起来往往使人意犹未尽啊。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页