Pintos project 1
小组成员:16281026 16281027
分工各占50%
Pintos:基于80x86架构的一个简单操作系统框架,支持内核线程,加载和运行用户程序,以及文件系统,但是所有这些都是以较为简单的方式实现。
在Ubuntu 16.04 LTS上运行。
(在Ubuntu 16.04 Kylin ,Ubuntu 18.04上boch安装失败)
通过QEMU仿真器加载pintos操作系统。命令如下:
sudo apt-get install qemu // install QEMU
将'pintos.gdb'文件中的变量GDBMACROS复制至HOME/os-
pg/pintos/src/misc/gdb-macros //Set GDBMACROS
cd $HOME/os-pg/pintos/src/utils ; make ; // Compile the Utilities
cd $HOME/os-pg/pintos/src/threads/ // Compile Pintos Kernel
export PATH=$HOME/os-pg/pintos/src/utils:$PATH // Add to Path variable
https://pintosiiith.wordpress.com/2012/09/13/install-pintos-with-qemu/
1.题目1:进程繁忙等待
Reimplement timer_sleep(), defined in devices/timer.c.
Although a working implementation is provided, it "busy waits," that is,it spins in a loop checking the current time and calling thread_yield() until enough time has gone by. Reimplement it to avoid busy waiting.
初始是使用“busy wait”方式实现的。
即线程不断循环,检查当前时刻并调用thread_yield()函数,直到时间片耗尽。
这种方式占用了大量的CPU资源!
重新实现的任务是避免“busy wait”,即繁忙等待现象的出现。
“busy wait”原理分析
void timer_sleep (int64_t ticks)
Suspends execution of the calling thread until time has advanced by at least x timer ticks. Unless the system is otherwise idle, the thread need not wake up after exactly x ticks. Just put it on the ready queue after they have waited for the right amount of time.
/* Number of timer ticks since OS booted. */
static int64_t ticks;
ticks 指的是pintos OS自启动以来,系统执行时间的单位量
/* Sleeps for approximately TICKS timer ticks. Interrupts must be turned on. */
void timer_sleep (int64_t ticks)
{
int64_t start = timer_ticks (); //获取ticks的当前值,即时间位置
ASSERT (intr_get_level () == INTR_ON); //断言可以被软中断,否则死循环
while (timer_elapsed (start) < ticks) //在ticks的时间内不断执行
thread_yield();
}
下面研究thread_yield()函数。
/* Yields the CPU. The current thread is not put to sleep and may be scheduled again immediately at the scheduler's whim. */
void thread_yield (void){
struct thread *cur = thread_current (); //获取当前线程的起始指针位置
enum intr_level old_level;
ASSERT (!intr_context ()); //断言为软中断
old_level = intr_disable ();
if (cur != idle_thread) //若当前线程并不空闲
list_push_back (&ready_list, &cur->elem);//把当前线程中的元素移至ready_list就绪队列
cur->status = THREAD_READY //线程状态改成THREAD_READY就绪状态
schedule ();
intr_set_level (old_level);
}
下面研究schedule()函数。
/* Schedules a new process. At entry, interrupts must be off and the running process's state must have been changed from running to some other state. This function finds another thread to run and switches to it. It's not safe to call printf() until thread_schedule_tail() has completed. */
static void schedule (void)
{
struct thread *cur = running_thread (); //获取当前线程cur
struct thread *next = next_thread_to_run (); //获取下一个要run的线程
struct thread *prev = NULL;
ASSERT (intr_get_level () == INTR_OFF); //同下
ASSERT (cur->status != THREAD_RUNNING); //原语操作,确保过程不被中断
ASSERT (is_thread (next)); //同上
if (cur != next) //若当前线程和下一个要run的线程不是同一个
prev = switch_threads (cur, next); //切换为cur当前线程
thread_schedule_tail (prev); //为下一个要run的线程分配资源等
}
综上所述,timer_sleep()的功能可以总结为;
在ticks时间内, 若当前线程cur处于running状态,就将其移动到ready_list中,阻止其继续执行。
后果:当前线程不断在就绪队列和执行状态中切换,即繁忙等待。很浪费系统资源!
解决方案:
在thread结构体中添加一个int成员blocked_ticks,以记录当前线程处于sleep状态的单位时间长度。
当执行timer_sleep()时,block当前进程。在每次ticks计数时,将ticks_blocked减一;
遍历检查所有已有线程,当ticks_blocked == 0时,调用thread_unlock()函数唤醒这一进程。
2.题目2:实现优先级调度alarm-priority
本题的要求是实现优先级调度priority scheduling。通过对timer_sleep函数的分析可以得到,我们可以通过维持ready_list就绪队列为一个优先级队列,即插入线程到ready_list时保证这个thread是一个优先级队列,来完成题目要求。
在thread.h中可以看到thread结构体中有优先级成员priority:
struct thread
{
/* Owned by thread.c. */
tid_t tid; /* Thread identifier. */
enum thread_status status; /* Thread state. */
char name[16]; /* Name (for debugging purposes). */
uint8_t *stack; /* Saved stack pointer. */
int priority; /* Priority. */
struct list_elem allelem;/* List element f