0. 引言
本文是对以下博文中Pintos Project1源代码的思路分析,截取了重要的核心函数,以方便读者理解整个的实现思路。
参考源代码: https://blog.csdn.net/denghuang8508/article/details/101357600
1. 第一部分相关
图1 第一部分相关核心函数调用关系图
1.1 timer_ticks()
timer_ticks (void)
{
enum intr_level old_level = intr_disable ();
int64_t t = ticks;
intr_set_level (old_level);
return t;
}
这里的enum intr_level old_level = intr_disable ()
和intr_set_level (old_level)
两句语句比较重要。简单来说,这两句我们将经常会在代码中看到,这两句语句成对出现,代表这两句语句之间的语句将被保证不会被中断,以维持原子性操作。
背后的原理就是这两句语句实现了:禁用了中断->保存现场->恢复原来中断状态的变化。
除去这两句语句,(就剩了一行)其实timer_ticks也就是一个返回了ticks当前数值的函数。从pintos被启动开始, ticks变量就一直在计时, 代表着操作系统执行单位时间的前进计量。也就是一个用于计时的变量。
1.2 schedule()
static void
schedule (void)
{
struct thread *cur = running_thread ();
struct thread *next = next_thread_to_run ();
struct thread *prev = NULL;
ASSERT (intr_get_level () == INTR_OFF);
ASSERT (cur->status != THREAD_RUNNING);
ASSERT (is_thread (next));
if (cur != next)
prev = switch_threads (cur, next);
thread_schedule_tail (prev);
}
schedule先把当前线程丢入就绪队列,切换就绪队列中下一个线程过来在CPU上运行。在切换之前,通过断言保证禁止中断、当前线程不处于运行态。若当前线程和下一个线程不同,则调用switch_thread
切换线程,最后调用thread_schedule_tail
恢复现场。
1.3 thread_yield()
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);//如果当前线程不是空闲的线程,则把当前线程的元素扔到就绪队列里
cur->status = THREAD_READY;
schedule ();
intr_set_level (old_level);
}
暂时挂起一个线程,等待被唤醒。挂起线程的实现原理:把当前线程扔到就绪队列里, 然后切换下一个线程进入CPU.
1.4 thread_block()
void
thread_block (void)
{
ASSERT (!intr_context ());
ASSERT (intr_get_level () == INTR_OFF);
thread_current ()->status = THREAD_BLOCKED;
schedule ();
}
当前线程设置为阻塞状态,如果没有thread_unblock
函数,将不会唤醒该线程。
1.5 timer_sleep()
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
阻塞该线程。线程结构体成员ticks_blocked代表这个线程睡眠的时间,方便之后调用检测函数blocked_thread_check
来判断该线程是否睡眠了足够时间。
1.6 thread_unblock()
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);
}
把阻塞的线程t放入就绪队列中。
1.7 blocked_thread_check()
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);
}
}
}
时间检测函数,每次调用该函数将线程的ticks_blocked减1, 如果减到0就调用thread_unblock
唤醒这个线程。这样保证了线程有足够的睡眠时间。
1.8 thread_foreach()
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);
}
}
顾名思义,就是让每个线程都调用func
函数,aux
为可选参数。
1.9 timer_interrupt()
static void
timer_interrupt (struct intr_frame *args UNUSED)
{
ticks++;
thread_tick ();
thread_foreach(blocked_thread_check,NULL);
}
时间中断处理函数,通过调用thread_foreach
对每个线程加入对它睡眠时间的检测函数blocked_thread_check
。
2. 第二部分相关
图2 第二部分相关