2019-06-12-pintos 实验1


layout: post
title: “pintos 实验一”
description: “”
date: 2019-06-12

文章目录

环境配置问题

1、Pintos安装问题

1)pintos版本过低导致编译出错

在这里插入图片描述

解决办法:

安装最新版本的pintos。可以 git clone git://pintos-os.org/pintos-anon,或者其它方式。

2)安装bochs时配置错误

解决办法:

主要是bochsGUI界面显示不出来导致无法执行测试命令,记录其安装位置,执行命令./configure –with -nogui –enable-gdb-stub 禁用其GUI,但是仍然可能无法解决问题,可以到test目录下更改Make文件使其不调用gui

2、Pintos运行问题

1)更改相关配置文件(或者命令行执行),添加执行路径

在这里插入图片描述

2)找不到kernel,loader问题解决

更改/pintos/src/utils/pintos文件
在275行, kernel.bin 改为 $HOME/ospg/pintos/src/threads/build/kernel.bin (或者为绝对路径)
更改/pintos/src/utils/Pintos.pm文件
在362行, loader.bin 改为 $HOME/ospg/pintos/src/threads/build/loader.bin (或者为绝对路径)

3)pintos没有启动

在这里插入图片描述

李许增 16281042

重新实现timer_sleep()函数

要求

重新实现timer_sleep(),在devices/timer.c中定义。虽然提供了一个工作实现,但它会“busy waits”,也就是说,它会不断循环检查当前时间并调用thread_yield()函数,直到有足够的时间过去。重新实现使其避免“busy waiting”。

实现思路

调用timer_sleep的时候直接把线程阻塞掉,然后给线程结构体加一个成员ticks_blocked来记录这个线程被sleep了多少时间, 然后利用操作系统自身的时钟中断(每个tick会执行一次)加入对线程状态的检测, 每次检测将ticks_blocked减1, 如果减到0就唤醒这个线程。
具体代码:

/* Sleeps for approximately TICKS timer ticks.  Interrupts must
   be turned on. */
void
timer_sleep (int64_t ticks)
{
   
  if (ticks <= 0)
  {
   
    return;
  }
  ASSERT (intr_get_level () == INTR_ON);
  enum intr_level old_level = intr_disable ();
  struct thread *current_thread = thread_current ();
  current_thread->ticks_blocked = ticks;
  thread_block ();
  intr_set_level (old_level);
}

注意这里调用的thread_block:

/* Puts the current thread to sleep.  It will not be scheduled
   again until awoken by thread_unblock().

   This function must be called with interrupts turned off.  It
   is usually a better idea to use one of the synchronization
   primitives in synch.h. */
void
thread_block (void)
{
   
  ASSERT (!intr_context ());
  ASSERT (intr_get_level () == INTR_OFF);

  thread_current ()->status = THREAD_BLOCKED;
  schedule ();
}

给线程的结构体加上我们的ticks_blocked成员:

 /* Record the time the thread has been blocked. */
int64_t ticks_blocked;

然后在线程被创建的时候初始化ticks_blocked为0, 加在thread_create函数内:

t->ticks_blocked = 0;

然后修改时钟中断处理函数, 加入线程sleep时间的检测, 加在timer_interrupt内:

thread_foreach (blocked_thread_check, NULL);

这里的thread_foreach就是对每个线程都执行blocked_thread_check这个函数:

/* Invoke function 'func' on all threads, passing along 'aux'.
   This function must be called with interrupts off. */
void
thread_foreach (thread_action_func *func, void *aux)
{
   
  struct list_elem *e;

  ASSERT (intr_get_level () == INTR_OFF);

  for (e = list_begin (&all_list); e != list_end (&all_list);
       e = list_next (e))
    {
   
      struct thread *t = list_entry (e, struct thread, allelem);
      func (t, aux);
    }
}

aux就是传给这个函数的参数。

然后, 给thread添加一个方法blocked_thread_check即可:

thread.h中声明:

void blocked_thread_check (struct thread *t, void *aux UNUSED);

thread.c:

/* Check the blocked thread */
void
blocked_thread_check (struct thread *t, void *aux UNUSED)
{
   
  if (t->status == THREAD_BLOCKED && t->ticks_blocked > 0)
  {
   
      t->ticks_blocked--;
      if (t->ticks_blocked == 0)
      {
   
          thread_unblock(t);
      }
  }
}

thread_unblock就是把线程丢到就绪队列里继续跑:

/* Transitions a blocked thread T to the ready-to-run state.
   This is an error if T is not blocked.  (Use thread_yield() to
   make the running thread ready.)

   This function does not preempt the running thread.  This can
   be important: if the caller had disabled interrupts itself,
   it may expect that it can atomically unblock a thread and
   update other data. */
void
thread_unblock (struct thread *t)
{
   
  enum intr_level old_level;

  ASSERT (is_thread (t));

  old_level = intr_disable ();
  ASSERT (t->status == THREAD_BLOCKED);
  list_push_back (&ready_list, &t->elem);
  t->status = THREAD_READY;
  intr_set_level (old_level);
}

这样timer_sleep函数唤醒机制就实现了。
测试结果:
在这里插入图片描述

实现优先级调度

要求

当一个线程被添加到具有比当前运行的线程更高优先级的就绪列表时,当前线程应该立即将处理器分给新线程中。类似地,当线程正在等待锁,信号量或条件变量时,应首先唤醒优先级最高的等待线程。线程应可以随时提高或降低自己的优先级,但降低其优先级而不再具有最高优先级时必须放弃CPU。

金睿琦 16281041

alarm_priority

分析

我们实现线程优先级调度需要时刻维持就绪队列为一个优先级队列,所以在插入线程到就绪队列的时候需要继续维持这个队列的优先级有序性质。我们研究发现在以下三种方法中会把一个线程插入到就绪队列中:

  1. thread_unblock
  2. init_thread
  3. thread_yield

我们可以找到thread_unblock方法中线程插入队列的代码:

list_push_back(&ready_list, &t->elem);

这段代码会把线程直接插入到队列尾部,而调度下一个thread会直接取队列头部,显然不符合优先级有序性。

我们研究发现pintos的队列实现中发现有这样的方法:

void list_insert_ordered(struct list *, struct list_elem *, list_less_func *, void *aux);

这个方法可以保证在插入队列后,队列中的线程仍然具有有序性,可以用于维持队列优先级有序性。

所以我们把thread_unblock方法中的list_push_back修改为:

list_insert_ordered(&ready_list, &t->elem, (list_less_func *) &thread_cmp_priority, NULL);

然后还需要实现比较函数:

bool thread_cmp_priority(const struct list_elem *a, const struct list_elem *b, void *aux UNUSED){
   
    return list_entry(a, struct thread, elem)->priority > list_entry(b, struct thread, elem)->priority;
}

最后再对thread_yield方法和thread_init方法作相同的修改。把init_thread方法中的list_push_back修改为:

list_insert_ordered(&all_list, &t->allelem, (list_less_func *) &thread_cmp_priority, NULL);

把thread_yield方法中的list_push_back修改为:

list_insert_ordered(&ready_list, &cur->elem, (list_less_func *) &thread_cmp_priority, NULL);

修改代码后,alarm_priority这个测试样例就可以pass了。

priority_change/priority_preempt:

分析

通过分析关于抢占式调度的测试样例:我们需要保证在创建一个新线程的时候,如果新线程优先级高于当前线程优先级,就要阻塞当前线程转为先执行新线程,所以每次设置一个新线程就要立即重新安排所有线程的优先级顺序。
我们的实现方案是在新线程设置优先级的时候调用thread_yield方法,这样就可以把当前线程重新丢到就绪队列中继续执行,相当于重新制定了所有线程优先级顺序,而且在创建新线程的时候,如果新线程比主线程优先级高也需要调用thread_yield方法。

修改thread_set_priority方法为:

void thread_set_priority(int new_priority){
   
    thread_current()->priority = new_priority;
    thread_yield();
}

还需要在thread_create方法把创建的新线程unblock后加上以下代码:

if (thread_current()->priority < priority){
   
    thread_yield();
}

修改代码后,priority_change/priority_preempt这两个测试样例就都可以pass了。

李亚远 16281043

TDD分析

priority-donate-one

分析

当发现高优先级的任务因为低优先级任务占用资源而阻塞时,就将低优先级任务的优先级提升到等待它所占有的资源的最高优先级任务的优先级。
先来看测试代码:

void
test_priority_donate_one (void) 
{
   
  struct lock lock;

  /* This test does not work with the MLFQS. */
  ASSERT (!thread_mlfqs);

  /* Make sure our priority is the default. */
  ASSERT (thread_get_priority () == PRI_DEFAULT);

  lock_init (&lock);
  lock_acquire (&lock);
  thread_create ("acquire1", PRI_DEFAULT + 1, acquire1_thread_func, &lock);
  msg ("This thread should have priority %d.  Actual priority: %d.",
       PRI_DEFAULT + 1, thread_get_priority ());
  thread_create ("acquire2", PRI_DEFAULT + 2, acquire2_thread_func, &lock);
  msg ("This thread should have priority %d.  Actual priority: %d.",
       PRI_DEFAULT + 2, thread_get_priority ());
  lock_release (&lock);
  msg ("acquire2, acquire1 must already have finished, in that order.");
  msg ("This should be the last line before finishing this test.");
}

static void
acquire1_thread_func (void *lock_) 
{
   
  struct lock *lock = lock_;

  lock_acquire (lock);
  msg ("acquire1: got the lock");
  lock_release (lock);
  msg ("acquire1: done");
}

static void
acquire2_thread_func (void *lock_) 
{
   
  struct lock *lock = lock_;

  lock_acquire (lock);
  msg ("acquire2: got the lock");
  lock_release (lock);
  msg ("acquire2: done");
}

分析: 首先当前线程(称为original_thread)是一个优先级为PRI_DEFAULT的线程, 然后第4行创建了一个锁, 接着创建一个线程acquire1,优先级为PRI_DEFAULT+1, 传了一个参数为这个锁的函数过去(线程acquire1执行的时候会调用)。
好, 我们之前实现的抢占式调度会让acquire1马上执行, 来看acquire1_thread_func干了什么, 这里直接获取了这个锁, 来看lock_acquire函数:

/* Acquires LOCK, sleeping until it becomes available if
   necessary.  The lock must not already be held by the current
   thread.

   This function may sleep, so it must not be called within an
   interrupt handler.  This function may be called with
   interrupts disabled, but interrupts will be turned back on if
   we need to sleep. */
void
lock_acquire (struct lock *lock)
{
   
  ASSERT (lock != NULL);
  ASSERT (!intr_context ());
  ASSERT (!lock_held_by_current_thread (lock));

  sema_down (&lock->semaphore)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值