Pintos Projects 1:Threads
前期安装、查询资料、mission1的实现(李许增)
小组组长:韩坤洋
小组成员:李许增,金睿琦,李亚远,陈 琦,黄睿东
一、项目要求
1、重新实现timer_sleep()函数
重新实现timer_sleep(),在devices/timer.c中定义。虽然提供了一个工作实现,但它会“busy waits”,也就是说,它会不断循环检查当前时间并调用thread_yield()函数,直到有足够的时间过去。重新实现使其避免“busy waiting”。
2、实现优先级调度
当一个线程被添加到具有比当前运行的线程更高优先级的就绪列表时,当前线程应该立即将处理器分给新线程中。类似地,当线程正在等待锁,信号量或条件变量时,应首先唤醒优先级最高的等待线程。线程应可以随时提高或降低自己的优先级,但降低其优先级而不再具有最高优先级时必须放弃CPU。
3、实现多级反馈调度
实现多级反馈队列调度程序,减少在系统上运行作业的平均响应时间。这里维持了64个队列, 每个队列对应一个优先级, 从PRI_MIN到PRI_MAX。通过一些公式计算来计算出线程当前的优先级, 系统调度的时候会从高优先级队列开始选择线程执行, 这里线程的优先级随着操作系统的运转数据而动态改变。
二、环境配置问题
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没有启动
本组使用的工具为qemu,应该注意更改pintos的默认启动设置,pintos默认为bochs启动,更改 …/utils/pintos 文件103行为 $sim = “qemu” if !defined $sim
并且更改 …/threads/Make.vars 文件中simulator的值 SIMULATOR = --qemu ,再次执行测试命令就正常执行
PART1实现
PART1:重新实现timer_sleep函数
安装过程:
实现思路:
调用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函数唤醒机制就实现了。
测试结果: