ImageVerifierCode 换一换
格式:DOCX , 页数:22 ,大小:192.20KB ,
资源ID:8473841      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/8473841.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(操作系统课程设计实验报告proj1.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

操作系统课程设计实验报告proj1.docx

1、操作系统课程设计实验报告proj1操作系统课程设计报告 班级: 团队成员: 目录目录 2一、 Nachos综述 3二、 实验要求:建立线程系统 32.1 Task 1.1实现KThread.join() 32.1.1题目要求 32.1.2题目分析与实现方案 32.1.3关键点与难点 42.1.4实现代码 42.2 Task1.2实现条件变量 52.2.1题目要求 52.2.2题目分析与实现方案 52.2.3关键点与难点 62.2.4实现代码 72.3 Task1.3完成Alarm类 72.3.1题目要求 72.3.2题目分析与实现方案 82.3.3关键点与难点 92.3.4实现代码 92.4

2、Task 1.4条件变量实现同步消息 102.4.1题目要求 102.4.2题目分析与实现方案 102.4.3关键点与难点 112.4.4实现代码 112.5 Task 1.5实现优先级调度 132.5.1题目要求 132.5.2题目分析与实现方案 132.5.3关键点与难点 142.5.4实现代码 152.6 Task 1.6条件变量解决过河问题 162.6.1题目要求 162.6.2题目分析与实现方案 172.6.3关键点与难点 192.6.4实现代码 19三、 测试结果 23一、 Nachos综述Nachos是一款教学用的操作系统平台,他的全名叫做 “NotAnotherComplete

3、lyHeuristicOperatingSystem”,Nachos的运行必须借助于宿主机,它通过软件模拟了各种硬件系统,包括中断系统、存储系统、磁盘文件、网络等。它的运行是可以跟踪的,因此,我们可以一步一步的观察操作系统是如何运行的。 Nachos操作系统本身只提供了一套框架,很多地方的实现都需要我们自己来完善,因此我们可以通过修改其源代码,来丰富和增强Nachos操作系统的功能.更可以在完善这些功能的同时,了解操作系统的内部运行机制。二、实验要求:建立线程系统2.1 Task 1.1:实现KThread.join()2.11题目要求 实现Implement Kthread.join()函数

4、。其它的线程不必调用join函数,但是如果join()函数被调用的话,也只能被调用一次。对join()函数第二次调用的执行结果是不被定义的,即使第二次调用的线程与第一次调用的线程不同。无论有没有被join,一个进程都能够正常结束。2.1.2题目分析与实现方案分析:join函数的作用即为等待调用此函数线程运行完毕。当前线程A调用另一个线程(处于就绪状态) B的join函数时,即A执行B.join()时 (A和B在Nachos中均为Kthread类型对象), A被挂起, 直到B运行结束后, join函数返回, A才能继续运行。如果这个线程已经结束,马上返回。且join()函数只能被调用一次,第二次

5、调用不能保证返回,且调用join()函数的线程必须不能是当前线程。Join()函数结束时唤醒在等待队列中的所有线程,因此需要实现join()方法和修改finish()方法。 方案: (1)Kthread的join()中的Lib.assertTrue(this != currentThread)已经实现线程只能调用一次join()方法,根据要求,在调用join()方法时,让当前运行线程休眠,并将当前运行的线程加入到一个阻塞队列中。(2)在线程结束时,finish()函数循环唤醒所有被阻塞的线程。Finish()函数在run()函数返回时自动调用,同样可以被直接调用。但当前唯一运行的线程不能被fi

6、nish()函数立即结束,只有当其他线程运行时才可结束。2.1.3关键点与难点由于java派生于抽象类的对象无法实例化,在运行时很可能出现空指针异常。只能直接利用抽象类的对象调用相关方法。2.1.4实现代码public void join() Lib.debug(dbgThread, “Joining to thread: “ + toString(); / 判断要调用的进程是否为当前进程(线程只能调用一次join()方法) Lib.assertTrue(this != currentThread); / 系统关中断,当前线程休眠 _oolean intStatus = Machine.int

7、errupt().disable(); if (status != statusFinished) / 调用另一个要调用的进程,将当前运行的线程加入到一个阻塞队列中。 waitForJoin.waitForAccess(currentThread); / 当前进程睡眠等待被调用进程结束 sleep(); Machine.interrupt().restore(intStatus); public static void finish() Lib.debug(dbgThread, “Finishing thread: “ + currentThread.toString(); / 系统关中断 M

8、achine.interrupt().disable();/ 当前进程运行结束的标志 Machine.autoGrader().finishingCurrentThread(); Lib.assertTrue(toBeDestroyed = null); / 表示当前进程要结束了 toBeDestroyed = currentThread; / 当前进程的状态被修改为运行结束 currentThread.status = statusFinished; / 调用等待队列上的第一个进程 Kthread waitThread; while (waitThread = currentThread.w

9、aitForJoin.nextThread() != null) / 唤醒等待队列上所有被阻塞的进程 waitThread.ready(); sleep(); 2.2 Task1.2实现条件变量2.2.1题目要求利用中断有效和无效所提供的原子性直接实现条件变量。我们已经提供类似的例子实例实现信号量。你要按此提供类似的条件变量的实现,不能直接利用信号量来实现(你可以使用lock,虽然它间接地调用了信号量)。在你完成时要提供条件变量的两种实现方法。你的第二种条件变量实现要放在nachos.threads.Condition2中。2.2.2题目分析与实现方案分析:条件变量使我们可以睡眠等待某种条件出

10、现,是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。 方案:threads.Lock类提供了锁以保证互斥。在临界代码区的两端执行Lock.acquire() 和Lock.release() 即可保证同时只有一个线程访问临界代码区。条件变量建立在锁之上, 由threads.Condition实现,用来保证同步。每一个条件变量拥有一个锁变量。 (1)sleep()在条件变量的控制下sleep()函数自动释放相关锁并进入睡眠状态,直到令一个线

11、程用wake()唤醒它,需要在函数的开始和结尾处关或允许中断保证原子性。当前线程必须拥有相关锁,阻塞进程在sleep()函数返回之前线程将会自动再次拥有锁。即调用这个方法的线程将自己挂起到等待队列,阻塞自己的同时将自己拥有的锁交出去,之后等待锁的分配。拿到锁的线程再次拥有权限接受检验。 (2)wake()唤醒条件变量队列中第一个线程,在试验中利用从线程队列中取出线程用Kthread.ready()实现唤醒,加入就绪队列。(同样要在wake函数利用中断保证原子性)(3)wakeall()函数的实现依赖于wake()。只需不断地wake挂在条件变量上的线程直到队列为空为止。2.2.3关键点与难点题

12、目要求直接实现条件变量,不能直接利用信号量。所以互斥锁必须要有,它是线程们有秩序的接受条件变量检验的保证。等待队列必须要有,不满足条件的线程都要挂在上面。2.2.4实现代码public static void sleep() Lib.debug(dbgThread, “Sleeping thread: “ + currentThread.toString(); Lib.assertTrue(Machine.interrupt().disabled(); if (currentThread.status != statusFinished) / 锁住当前线程 currentThread.stat

13、us = statusBlocked; / 运行下一个线程 runNextThread(); public void wake() Lib.assertTrue(conditionLock.isHeldByCurrentThread(); if (!waitQueue.isEmpty() (Semaphore) waitQueue.removeFirst().V();public void wakeAll() Lib.assertTrue(conditionLock.isHeldByCurrentThread(); while (!waitQueue.isEmpty() wake();2.3T

14、ask1.3完成Alarm类2.3.1题目要求完成Alarm类,通过waitUntil(longx)方法实现。一个线程通过调用waitUntil(longx)方法将自己挂起,一直到经过x时间再被唤醒。这对现实操作很有用,例如,光标的周期闪烁。线程经过x时间后唤醒,但不需要立刻运行,而是加入readyqueue中。不建立任何多余的线程实现waitUntil(),只需要修改waitUntil()中断处理程序和计时器。waitUntil()不受限于任何一个线程,任何线程可以调用它实现被挂起。2.3.2题目分析与实现方案分析:Alarm类使用硬件定时器提供抢占,并允许线程挂起到某个时间。分配的新闹钟设

15、置机器的定时器中断处理程序实现本闹钟的回调,同时Nachos只在有一个计时器的情况下正常运行。定时器中断处理程序被称为机器计时器定期(大约每500时钟周期)。当前线程产生后,如果有另一个必须运行的线程,则强制上下文切换。 当前线程睡眠至少x时间周期,在定时器中断处理程序中将其唤醒。当现在时间(current time) = (WaitUntil called time)+(x)时,线程必须被唤醒(在调度准备集)在第一个定时器中断的产生时。 方案:(1)与Alarm类有关的是machine.Timer类,它在大约每500个时钟滴答使调用回调函数(由Timer.setInterruptHandle

16、r函数设置)。因此, Alarm类的构造函数中首先要设置该回调函数Alarm.timerInterrupt()。 timerInterrupt()方法在每一次timer产生时间中断时遍历队列,检查队列中的时间状态,当线程到了等待的时间就把线程从队列中取出放入就绪队列。 (2)waitUntil()方法使用了一个队列可以存放线程以及唤醒时间,这个队列以时间为序的有序队列。每次调用时,把当前线程和唤醒时间加入队列,等待唤醒。在调用waitUntil(x) 函数时, 首先得到关于该线程的信息(线程: 当前线程, 唤醒时间: 当前时间+x), 该线程设定唤醒时间并阻塞,并调用sleep操作使当前线程挂

17、起, 放入队列中。在时钟回调函数中(大约每500个时钟间隔调用一次) 有一次中断,中断时则依次检查队列中的每个对象,将队列中此时应当唤醒的进程加入就绪队列。如果唤醒时间大于当前时间, 则将该对象移出队列并执行wake操作将相应线程唤醒。 (3)Waiter()是一个用来管理和存储多个Kthread及其唤醒时间的内部类,属性分别为该Kthread与其唤醒时刻waketime。2.3.3关键点与难点线程调用waitUntil方法之后会终止,该线程设定唤醒时间并阻塞,直到传入的时间之后才可以执行。线程只是放入就绪队列,等待分配。可以使用一个线程队列,但是不能产生额外的线程。Timer每过500个cl

18、ockticks会产生一个timeInterrupt。timeInterupt时,会运行handle()线程,将handler线程功能设置为将队列中此时应当唤醒的进程加入就绪队列。2.3.4实现代码 class Waiter Waiter(long wakeTime, Kthread thread) this.wakeTime = wakeTime; this.thread = thread; private long wakeTime; / 唤醒时间 private Kthread thread; / 等待的线程 private LinkedList waitlist; / 存放线程以及唤醒

19、时间public void waitUntil(long x) / 系统关中断 _oolean intStatus = Machine.interrupt().disable(); / for now, cheat just to get something working (busy waiting is bad) / 确定唤醒的时间 long wakeTime = Machine.timer().getTime() + x; Waiter waiter = new Waiter(wakeTime, Kthread.currentThread(); / 将线程加入到等待队列上 waitlis

20、t.add(waiter); System.out.println(“线程:” + Kthread.currentThread().getName() + “t休眠时间:” + Machine.timer().getTime() + “t下次唤醒时间:” + wakeTime);/ 当前线程设定唤醒时间并阻塞,挂起 Kthread.sleep();/ 开中断 Machine.interrupt().restore(intStatus); 2.4 Task 1.4条件变量实现同步消息2.4.1题目要求使用条件变量来实现一个字长信息的发送和接收同步。使用void speak(int word) 和

21、int listen()函数来实现通讯(Communicator)类的通讯操作。Speak函数具有原子性,在相同地Communicator类中等待listen函数被调用,然后将此字发生给listen函数。一旦传送完毕,两个函数都返回(listen函数返回此字)。2.4.2题目分析与实现方案解决方案要满足使用同一个通讯对象实例中多个speaker和listener能够相互通讯。(注意:这种情况等于0字长的缓冲区;既然缓冲区没用空间,那么需要生产者和消费者直接进行交互,要求他们相互等待)。每一个通讯实例只能使用一个lock类,如果使用多于一个lock类,会将事情复杂化。 每个Communicato

22、r拥有的锁(保证操作的原子性) 和与该锁联系的两个条件变量用于保证speaker和listener间的同步。在speak函数中, 首先检查是否已经有speaker在等待(speaknum0) 或无listener等待,满足这两种情况就挂起。 若不满足,则设置变量, 准备数据并唤醒一个listener。 在listen函数中, 增加一个listener后, 首先, 然后这个问题其实是一个缓冲区长度为0的生产者/消费者问题。 Speak():先获得锁,然后进行判断,如果没有听者等待,就要把说者放入队列然后睡眠。如果有听者等待,就要唤醒一个听者,然后传递消息,最后释放锁。 Listen():先获得锁

23、,然后进行判断尝试唤醒speaker,如果没有说者等待,就要把听者放入队列然后睡眠。如果有说者等待,就要唤醒一个说者,将自己挂起以等待speaker准备好数据再将自己唤醒,然后传递消息,最后释放锁。2.4.3关键点与难点 当有多个speaker和listener时,它们也只能是一对一的, 即一个speaker只能将数据发送到一个listener, 一个listener也只能接收来自一个speaker的数据, 其余的speakers和listeners都需要等待。多个speaker阻塞时需要保存每一个speaker的word,阻塞的speaker挂在Condition对象的Queue队列上。因为

24、无缓冲区,listener唤醒说者后要先挂起,消息在speaker准备好后才传递。2.4.4实现代码public class Communicator public Communicator() lock = new Lock(); speaker = new Condition2(lock); listener = new Condition2(lock); private Lock lock; / 互斥锁 private int word = 0; private static int speakercount = 0; / 说者数量 private static int listener

25、count = 0; / 听者数量 LinkedList Wordqueue = new LinkedList();/ 保存说者话语的队列 Condition2 speaker;/ 说者条件变量 Condition2 listener;/ 听者条件变量/*先获得锁,然后进行判断,如果没有听者等待,就要把说者放入队列然后睡眠。 如果有听者等待,就要唤醒一个听者,然后传递消息,最后释放锁*/ public void speak(int word) / 系统关中断 _oolean intStatus = Machine.interrupt().disable(); / 拿到锁 lock.acquir

26、e();/ 没有听者,说者睡眠,队列存储说者的话 if (listenercount = 0) speakercount+; Wordqueue.offer(word); speaker.sleep();/ 尝试唤醒听者 listener.wake();/ 说者队列人数减一 speakercount-; else / 准备消息,唤醒听者 Wordqueue.offer(word); listener.wake(); / 释放锁 lock.release();/ 系统开中断 Machine.interrupt().restore(intStatus); System.out.println(Kt

27、hread.currentThread().getName() + “ 发出信息 “ + word); return; public int listen() / 系统关中断 _oolean intStatus = Machine.interrupt().disable();/ 获得锁 lock.acquire(); if (speakercount != 0) speaker.wake(); listener.sleep(); else listenercount+; listener.sleep(); listenercount-; System.out.println(Kthread.c

28、urrentThread().getName() + “ 收到信息 “ + Wordqueue.getFirst(); System.out.println(); lock.release(); Machine.interrupt().restore(intStatus); return Wordqueue.poll(); 2.5 Task 1.5实现优先级调度 2.5.1题目要求 通过完成实现PriorityScheduler优先级调度策略。所有的调度程序都是继承自Scheduler类,所以必须实现getPriority(), getEffectivePriority()和setPriori

29、ty()方法。2.5.2题目分析与实现方案分析:Nachos系统已经提供了一个简单的轮转调度器,采用简单的FIFO队列进行调度。优先级调度的传统算法如下: 每个线程拥有一个优先级(在Nachos中, 优先级是一个0到7之间的整数, 默认为1)。在线程调度时, 调度程序选择一个拥有最高优先级的处于就绪状态的线程运行。这种算法的问题是可能出现“饥饿” 现象。为解决上述优先级反转问题, 需要实现一种“让出”优先级的机制(Priority Donation) 。getEffectivePriority()就是为了解决优先级翻转而设的,也就是说当出现一个优先级高的线程等待低优先级线程时,若存在另外的高优

30、先级线程而导致低优先级线程永远不能执行的情况,该低优先级线程调用该函数返回它持有的锁上等待的所有线程中优先级中最高的一个给该优先级线程,以确保它执行。 方案: 在引入优先级的同时就需要引入一个ThreadState对象,它用来把线程和优先级绑定在一起。而且内部还有一个队列用来记录等待它的线程。在 ThreadState类中增加两个LinkedList类表,存放PriorityQueue。一个表用来记录该线程所占用资源的优先队列,另一个表用来记录该线程所想占有的资源的优先队列。占用资源的队列作为发生优先级反转时,捐献优先级计算有效优先级的来源依据,想占有的资源的队列用来为线程声明得到资源做准备。 getEffectivePriority():计算有效优先级时,遍历等待队列中所用线程的有效优先级,找出最大的优先级。首先要在PriorityQueue类下创建个装有Kthread的LinkedList,即等待队列waitQueue,声明一个effectivePriority,遍历waitQueue,找出priority最大的那个Kthread,将它的priority赋给effectivePriority,然后返回即可。 而在队列类中最重要的就是nextThread()方法,它

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1