I am analyzing a schduler of Pintos ( I mean- the simple scheduler provided by the native implementation).
When a tick ( I mean tick raised by timer) happen it will be handled by:
static void
timer_interrupt (struct intr_frame *args)
{
ticks++;
thread_tick ();
}
struct pointed by args
contains ( among others) IP
of the next instruction interrupted thread. Therefore, it is crucial to remember it for the current thread to let him to continue his job in the future. Please note, that handler only increments ticks
and calls thread_tick()
without args
. Now, the information about eip
( and segments registers etc.) is lost.
Does it mean that the provided scheduler can't make possible continue thread's job? In that situation it may be a part of the first project.
I am asking because I am not sure if I misunderstand something.
I attach an "entry-point" of every interrupt handler:
.func intr_entry
intr_entry:
/* Save caller's registers. */
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushal
/* Set up kernel environment. */
cld /* String instructions go upward. */
mov $SEL_KDSEG, %eax /* Initialize segment registers. */
mov %eax, %ds
mov %eax, %es
leal 56(%esp), %ebp /* Set up frame pointer. */
/* Call interrupt handler. */
pushl %esp
.globl intr_handler
call intr_handler
addl $4, %esp
.endfunc
intr_handler
function:
void intr_handler (struct intr_frame *frame)
{
bool external;
intr_handler_func *handler;
external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
if (external)
{
ASSERT (intr_get_level () == INTR_OFF);
ASSERT (!intr_context ());
in_external_intr = true;
yield_on_return = false;
}
handler = intr_handlers[frame->vec_no];
if (handler != NULL)
handler (frame);
else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
{
// ignore
}
else
unexpected_interrupt (frame);
/* Complete the processing of an external interrupt. */
if (external)
{
ASSERT (intr_get_level () == INTR_OFF);
ASSERT (intr_context ());
in_external_intr = false;
pic_end_of_interrupt (frame->vec_no);
if (yield_on_return)
thread_yield ();
}
}
So, as you can see intr_handler
doesn't take care of context at all.
Basically, it calls a specific handler, like timer_interrupt
( see above ) . timer_interrupt
calls thread_tick
:
void thread_tick (void)
{
struct thread *t = thread_current ();
/* Update statistics. */
if (t == idle_thread)
idle_ticks++;
else
kernel_ticks++;
/* Enforce preemption. */
if (++thread_ticks >= TIME_SLICE)
intr_yield_on_return ();
}
and intr_yield_on_return
:
intr_yield_on_return (void)
{
ASSERT (intr_context ());
yield_on_return = true;
}
intr_yield_on_return
set the flag. intr_handler
has a piece of code:
if (yield_on_return)
thread_yield ();
so it calls thread_yield
:
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);
}
thread_yield
also doesn't remeber IP
( and others) for current thread.