77范文网 - 专业文章范例文档资料分享平台

nachos代码阅读--xq(2)

来源:网络收集 时间:2019-04-21 下载这篇文档 手机版
说明:文章内容仅供预览,部分内容可能不全,需要完整文档或者需要复制内容,请下载word后使用。下载word有问题请添加微信号:或QQ: 处理(尽可能给您提供完整文档),感谢您的支持与谅解。点击这里给我发消息

以开中断、调用(*func)(arg)以及调用Thread::Finish函数; 13) SaveUserState/RestoreUserState:在上下文切换时保护/恢复CPU状态; G. Threadtest.cc:

工作机制是创建两个线程,通过调用Yield()函数频繁切换上下文,显示内部线程的工作过程;

1) SimpleThread():循环5次,让出CPU给其它准备好的线程,参数int which是

为了调试用;

2) ThreadTest1():在两个线程之间设置ping-pong,fork一个线程调用

SimpleThread()函数,再自己调用SimpThread()函数;

3) ThreadTest():通过检查参数int testnum是否是1决定是否调用测试代码,即

调用ThreadTest1()函数;

H. Scheduler.h:

线程分发器和调度器的数据结构,包括一个就绪队列; I. Scheduler.cc:

选择下一个要运行的线程,并且调度该线程。这些代码假设中断已关来实现互斥。在这里我们不能使用锁来提供互斥机制,因为我们需要等待一个锁,而且这个锁是被占用的,我们最后会调用FindNextToRun(),这会使我们进入一个无限的循环。 1) Scheduler():初始化就绪队列为空; 2) ~scheduler():删除就绪线程队列;

3) ReadyToRun():标记一个线程为ready,放入就绪队列末尾;

4) FindNextToRun():返回下一个将要运行的线程,没有则返回NULL,同时从就绪

队列删除该线程;

5) Run():把CPU分发给下一个运行线程。调用switch代码以保存旧线程状态,同

时加载新线程状态;

a) 保存当前线程指针为oldThread;

b) 如果是用户程序,则保存用户的CPU寄存器;

c) 检查oldThread是否栈溢出(因为不是时刻检查栈段溢出,所以这时线程的

运行可能已经出错);

d) 当前线程置为传进来的参数nextThread; e) 当先线程状态设为Running;

f) 调用switch,切换新旧线程的状态(之前运城在当先线程的栈空间,之后运

行在nextThread的栈空间);

g) 如果旧线程因为结束放弃运行,即被赋给threadToBeDestroyed,则删除旧

线程;

h) 如果是用户程序,则恢复当前线程的地址空间;

6) Print():打印调度器的状态,即就绪队列的内容,用于调试; J. Switch.s:

与机器有关的上下文切换代码。说是机器相关的,是因为需要保存的寄存器、设置初始的调用函数栈空间等对于每一个处理器架构都是特别的。此处为每种架构设计了两段代码:

1) ThreadRoot(InitialPC, InitialArg, WhenDonePC, StartupPC):

a) InitialPC:将要运行的线程的入口函数的地址; b) InitialArg:入口函数的参数;

c) WhenDonePC:当该线程运行结束时要调用的代码;

d) StartupPC:运行该线程需要调用的代码,即要做的一些初始化工作; e) 在nachos源代码中,没有函数和方法显式调用此函数,ThreadRoot函数只

有在线程切换时才被调用。线程在初始化的最后准备工作要调用StackAllocate方法,该方法设置了几个寄存器的值,该线程第一次被切换时调用的就是ThreadRoot函数。其工作流程为: [1]. 清除帧指针;

[2]. 调用StartupPC函数;

[3]. 设置InitialArg参数,调用InitialPC函数,主程序; [4]. 调用WhenDonePC函数,结束时调用clean up程序;

f) 由此,ThreadRoot入口可以转而运行线程所要运行的函数,从而生成一个新

的线程;

2) SWITCH(oldThread, newThread):

a) oldThread:当前运行的线程,需要保存其CPU寄存器状态; b) newThread:将要运行的线程,需要加载其CPU寄存器状态;

c) 在Makefile.dep下定义了HOST = -DHOST_i386,所以只需看最下面的汇编

代码; d) 工作流程是:

[1]. 保存当前线程的状态:栈指针、被调用者保存的寄存器、帧指针、返回

地址;

[2]. 加载将要运行的线程状态:栈指针、被调用者保存的寄存器、帧指针、

返回地址;

[3]. 跳转到新运行的线程的栈空间运行新线程;

K. Synch.h:

同步线程的数据结构。这里定义了三种类型:信号量、锁和条件变量。已经实现了信号量,且给出了锁和条件变量的接口。此处每个同步对象都有一个name便于调试。 1) Class Semaphore:

定义了一个非负整数value,包括一个在P处的等待队列queue。还有一个函数bool isHeldByCurrentThread(),检查如果是currentThread持有该锁则返回true,用于检查Release时用。只有两个原子操作P和V: a) P():等待直到value>0。然后减1; b) V():加1,唤醒一个正在P()等待的线程;

此处不允许直接读信号量的值,即使读也是旧的值,不知道最新的值是多少,因为从寄存器读取时,可能发生了上下文切换,也可能有其它线程修改了信号量的值。

2) Class Lock:

锁机制是线程进入临界区的工具。一个锁有busy和free两种状态,当锁处于free时,线程可以申请取得锁进入临界区,执行完临界区代码后释放锁;当锁处于busy时,需申请该锁的线程进入睡眠态,等到锁为free时再申请取得锁。包含两个原子操作Acquire和Release,而且只有得到锁的线程才可以释放它:

a) Acquire():锁为busy时进入睡眠等待;锁为free时,线程获得锁设置为

busy继续运行;

b) Release():设置锁为free,如有等待线程,唤醒其中一个线程进入就绪态; 和信号量类似,不允许直接读锁的值value,因为读过之后马上就可能改变。另外,在这里可以添加需要实现锁自定义的变量。

3) Class Condition:

条件变量没有值,当一个线程需要的某种条件没有满足时,可作为一个等待条件变量的线程插入所有等待该条件变量的队列,只要条件满足就会被唤醒继续运行。条件变量总是和锁机制一同使用,在条件变量上有三个操作(所有这些操作必须在currentThread持有一个锁的前提下,且所有对一个条件变量进行的操作必须建立在同一个锁的前提下,保证访问该条件变量的线程之间的互斥):

a) Wait():释放锁,放弃CPU进入睡眠等待直到接收到信号,然后再次申请锁。

其中,释放锁和进入睡眠都是原子操作;

b) Signal():唤醒一个等待该条件变量的线程(如果存在); c) Broadcast():唤醒所有等待该条件变量的线程(如果存在);

在nachos中,所有的条件变量默认是遵循Mesa规范——当调用Signal或者Broadcast唤醒一个线程时,该线程置于等待链表,该线程调用Wait等待再次获得锁。不同于Hoare规范——发送信号的线程放弃锁和CPU给唤醒的线程,唤醒的线程立即运行,然后在运行至临界区之外时将锁给先前发送信号的线程。 使用Mesa规范导致在被唤醒的线程得到机会运行之前,可能有其它线程得到这个锁并且改变数据结构。

L. Synch.cc:

线程同步的代码。关于同步代码的实现需要一些原语的原子操作。我们假定nachos运行在单处理器上,因此原子操作可以通过关中断来实现。关闭中断后,没有上下文切换,currentThread确保一直占用CPU,直到中断打开。

一些代码调用时中断可能已关闭(比如信号量的V操作),我们直接重设中断状态到它原来的状态,而不是在原子操作的最后打开中断。 1) Class Semaphore:

a) Semaphore(char* debugName, int initialValue):构造函数,初始化一个

信号量;

b) ~Semaphore():当没有线程等待该信号量时,删除该信号量;

c) P():关中断,循环判断value的值,如果等于0则插入该信号量的等待队

列queue,然后调用sleep进入睡眠状态并切换到其它线程运行,唤醒之后继续检查value的值。检查到value>0,则跳出循环将value减1,然后恢复中断至原来的状态;

d) V():关中断,如果线程等待队列中有等待该信号量的线程,取出队首线程,

不为空则设为ready准备运行,然后立即将value加1,最后恢复中断状态;

2) 关于Lock和Condition的函数需要后面完善; M. Synchlist.h:

定义了对一个链表同时访问时的数据结构。通过在抽象链表数据结构前后添加同步代码来完善。

Class SynchList定义了同步的链表,该链表满足约束条件: 1) 试图从链表删除一个元素,必须等待链表中含有一个元素; 2) 每个时刻只有一个线程可以访问链表数据结构;

私有变量包括list链表(一个未同步的链表)、lock锁(保证对链表的互斥访问)、listEmpty条件变量(链表为空时在Remove处等待)。 N. Synchlist.cc:

用管程monitor的方式实现——每个程序都用一个锁的获得和释放对维护,同时利用条件变量的Signal和Wait来同步。

1) SynchList():为一个同步链表分配和初始化一个数据结构,初始链表为空; 2) ~ SynchList():删除为一个同步链表分配的资源;

3) Append():在链表末尾添加一个元素item,并唤醒所以等待添加元素的线程,

item是将要放进链表的东西,也可以是一个指针。执行过程是尝试申请取得锁(保证互斥访问)->添加元素item->唤醒等待的线程->释放锁;

4) Remove():删除链表的开头元素,若链表为空等待,最后返回删除的元素。执行

过程为尝试申请取得锁->循环检查链表为空则等待->链表不为空,则删除开头元素->释放锁->返回该元素;

5) Mapcar():将函数应用于链表的每个元素,遵循互斥的限制。执行过程为尝试申

请取得锁->调用List类的Mapcar->释放锁;

O.

4. /nachos-3.4/code/machine:

A. Timer.h:

定义了模拟硬件时钟的数据结构。一个硬件时钟每x毫秒,这可以用来给时间分片,或者让一个线程睡眠一定长的时间段。

此处模拟硬件时钟的思路是每次TimerTicks使得变量stats->totalTicks增加就产生一个终端。为了在时间分片上产生一定的随机性,可以设置变量‘doRandom’,然后在随机的ticks之后产生中断。

不能改编代码,因为这是机器模拟的一部分。 Timer.cc:

只是为了模拟一个nachos运行的硬件,并不是nachos系统的部分。所以不要改变: 1) static void TimerHandler(int arg):伪处理中断的函数;

a) 在Timer的初始化函数中,调用此内部函数;

b) 不直接使用初始化函数中的timeHandler参数作为中断处理函数的原因:如

果直接使用

timeHandler

作为时钟中断处理函数,

(int)

this,

interrupt->Schedule(TimerHandler, TimeOfNextInterrupt(),

TimerInt)是将一个时钟中断插入等待处理中断队列,一旦中断时刻到来则立即处理中断,处理结束后没有机会将下一个时钟中断插入等待处理的中断队列。而时钟中断时刻到来,调用TimerHandler内部函数,TimerHandler则调用TimerExpired成员函数,该函数可以将新的中断插入到等待处理的中断队列,然后再调用真正的时钟中断处理函数;

c) 不讲TimerExpired函数作为时钟中断在Timer的初始化函数中调用的原因:

C++不能直接引用一个类成员函数的指针,所以借用TimerHandler内部函数,也因此将TimerExpired设置为public属性;

2) Timer:初始化一个硬件时钟设备,每次中断,保存要调用的地址,然后安排时钟

开始产生中断;

3) TimerExpired():模拟产生中断的代码。调度下一次中断,并且调用中断处理函

数;

4) TimeOfNextInterrupt():返回下一次时钟中断的时间。如果设置了随机查宿,

则产生一个伪随机的延迟;

B. Interrupt.h:

定义模拟底层中断硬件的数据结构。硬件提供了代码来开关中断,可以设置SetLevel。为了模拟硬件,我们需要记录硬件设备将要产生的中断,以及它们什么时候应该产生。

这个模块也要记录模拟时间。只有一下三种情况才会增加时间:

? ? ?

再次开中断; 执行一条用户指令; 就绪队列为空;

因此,不同于真实的硬件,中断不能在关闭中断的代码中产生。这意味着不正确的同步代码可能在硬件模拟器上正常工作,但是在真实的硬件上则不然。 不能改变此处的代码,这是机器模拟的一部分。 1) IntStatus:枚举变量,包括开/关中断;

2) MachineStatus:枚举变量,包括空闲/系统/用户模式;

3) IntType:枚举变量,记录了产生中断的硬件设备,包括时钟、磁盘、控制台、网

络接收和发送;

4) PendingInterrupt:类定义了未来要产生的中断,内部数据结构均为public属性,

便于操作,包括产生中断时要调用的函数指针、传递的参数、产生的中断时间,以及初始化中断的函数;

5) Interrupt:类定义了模拟硬件中断的数据结构。记录中断是否打开,以及未来所

以的硬件中断; Interrupt.cc:

1) PendingInterrupt::PendingInterrupt():初始化未来要调度的硬件设备中断; 2) Interrupt::Interrupt():初始化硬件设备中断,即中断关闭,清楚inHandler

标志,初始为系统态,同时初始化等待处理的中断队列;

3) Interrupt::~ Interrupt():删除模拟中断所需的数据结构,循环释放等待处

理的中断队列中每个中断占用的空间资源;

4) Interrupt::ChangeLevel():改变中断类型为开/关,同时模拟时间不能增加,

是一个内置函数;

5) Interrupt::SetLevel():设置中断为开/关,而且如果设置打开中断,通过调

用OneTick()使得模拟时间增加。最后返回之前的中断开/关状态;

6) Interrupt::Enable():打开中断,不关心之前的状态,在ThreadRoot中用于

启动一个线程时打开中断;

7) Interrupt::OneTick():模拟时间增加一个单位,根据当前状态为用户态/系统

态时钟分别前进一个用户态时间单位/系统态时间单位,并更新nachos用户态和系统态运行的时间,同时检查当前是否还有中断发生,有则进行处理。有两种情况会调用此函数:

? ?

再次打开中断; 执行一条用户指令;

执行的过程如下: a) 保存旧状态;

b) 前进和前进时间单位; c) 关中断;

d) 等待将要发生的中断;

e) 如果yieldOnReturn为true,表示要切换上下文,则切换到内核模式,当前

线程调用Yield()函数让出CPU;

8) Interrupt::YieldOnReturn():在一个中断处理程序内部被调用,返回时在被

中断的线程引发一个上下文切换。在这里不能切换上下文,否则会置换掉中断处理函数,而我们想要置换被中断的线程。此函数内部设置yieldOnReturn标志;

百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库nachos代码阅读--xq(2)在线全文阅读。

nachos代码阅读--xq(2).doc 将本文的Word文档下载到电脑,方便复制、编辑、收藏和打印 下载失败或者文档不完整,请联系客服人员解决!
本文链接:https://www.77cn.com.cn/wenku/zonghe/613815.html(转载请注明文章来源)
Copyright © 2008-2022 免费范文网 版权所有
声明 :本网站尊重并保护知识产权,根据《信息网络传播权保护条例》,如果我们转载的作品侵犯了您的权利,请在一个月内通知我们,我们会及时删除。
客服QQ: 邮箱:tiandhx2@hotmail.com
苏ICP备16052595号-18
× 注册会员免费下载(下载后可以自由复制和排版)
注册会员下载
全站内容免费自由复制
注册会员下载
全站内容免费自由复制
注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: