3

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.

Gilgamesz
  • 4,727
  • 3
  • 28
  • 63
  • 1
    Presumably the interrupt entry-point saves context somewhere else before calling timer_interrupt with it. Obviously the rescheduling the current thread can't discard the current thread's state, but it's fine to discard an extra copy of a pointer to it. – Peter Cordes Sep 13 '16 at 22:59
  • @PeterCordes "Presumably the interrupt entry-point saves context somewhere else before calling timer_interrupt with it. " I edited the post. I've checked it before but it seems that entry-point doesn't save context in other place. It just pushes GP registers ( and semgent registers) and passes pointer as argument for the specific handler ( like `timer_interrupt`). "Obviously the rescheduling the current thread can't discard the current thread's state, but it's fine to discard an extra copy of a pointer to it.". Of course if it is an extra copy in fact. – Gilgamesz Sep 13 '16 at 23:19
  • 1
    Does that push of the user-space context happen at a known location that the kernel can find again later? I was talking about a copy of the pointer, not a copy of the whole context. – Peter Cordes Sep 13 '16 at 23:26
  • 1
    Another way to say that: since this is an OS that you know works, the right question is "where/when does the current context get saved?", not "does that mean the kernel won't be able to resume the current context?". I don't know PintOS at all, but I'm pretty confident that it does get saved somewhere that the OS can find it again later. The finding it again later may be a bit tricky, but looking at how context is restored on the return to user-space after a context switch might be a clue. – Peter Cordes Sep 14 '16 at 00:41
  • @MichaelPetch I edited my post. `intr_handler` doesn't seems to 'remeber' the context for current thread. "The bottom of the page actually contains current context information". It not true. Bottom of the page contains a 'struct thread' for current thread which is not context information. – Gilgamesz Sep 14 '16 at 13:03
  • 1
    The best place to get help for PintOS is probably talking to your professors and teacher assistants. They know the project. When you come here you will most likely find people who have general knowledge of OSes, but not specifically PintOS. People on SO basically have to wade through the exact same code you have (in its entirety) to follow the flow. This specific question may be answered by someone who will take the time and go through the code (including what you didn't post), but your best chances of good information is with your course material and instructors. You pay them for an education – Michael Petch Sep 14 '16 at 14:22
  • You may also get lucky that someone who did this project lurks on SO and happens to know off hand how it all works because they did the project themselves. I think those people will be rare (but not an impossibility) – Michael Petch Sep 14 '16 at 14:24
  • 2
    I've removed most of the comments I posted in the past hour and from last night. I realized that if I keep doing this you'll be inclined to ask very project specific problems on this site where the best answers probably come from your paid education. As it stands this question is not enough to provide an answer unless someone here happens to have done this project specifically. Maybe such a person is lurking on SO. But I don't think this is the place to ask this specific question. That is just my opinion. Other alternative: [#OSDev irc chat](https://kiwiirc.com/client/irc.freenode.net/#osdev) – Michael Petch Sep 14 '16 at 14:31
  • @PeterCordes Did you see that I edited the post: http://stackoverflow.com/questions/39439331/three-approaches-to-design-conditional-instructions I'm writing here because in my previous post I cannot notify you ( I don't know why). – Gilgamesz Sep 15 '16 at 07:10
  • @Gilgamesz: Yes, I noticed. Stop spamming me there by deleting and reposting your comment, please. It's not a very interesting question, and I haven't thought of anything I want to type up as an answer. – Peter Cordes Sep 15 '16 at 07:25
  • "Stop spamming me there by deleting and reposting your comment, please". I am sorry. I didn't know that it "spamming" you. I did it because I was convinced that you are not notified. ( When I put @PeterCordes it disappeared- this is why I wrote here). Obviously, I didn't want to spam you, don't treat it as a tactless behaviour – Gilgamesz Sep 15 '16 at 14:32
  • 1
    It wouldn't tab-complete `@PeterCordes` there because I hadn't yet commented on my own post. But it was my answer, so I was still notified. Thanks for letting me know why you kept reposting that comment. – Peter Cordes Sep 15 '16 at 15:04
  • 1
    All threads (kernel or user space) have 1 stack. That stack is 4kb with thread context information at the base. ALL threads are set to appear as if they running from `switch_threads` (see src/threads/switch.S). When an interrupt occurs a current thread will be interrupted. The interrupt handler `intr_entry:` pushes all the state information when it is first run onto the current threads stack. If the timer tick interrupt determines it is time to switch tasks it sets the global variable `yield_on_return` to _TRUE_ . – Michael Petch Sep 16 '16 at 02:21
  • 1
    The last thing the interrupt handler does is changes threads if need be if yield_on_return is TRUE. It does this by simply changing the stack pointer to that of the next thread to execute. That thread will have its own state that was saved by a previous timer tick interrupt (or created manually at thread creation). When`thread_yield() finishes it is operating on the stack of the next thread to get a time slice. The `iret` instruction will ultimately restore the state that was previously saved when that thread was yielded (or the process was first created) – Michael Petch Sep 16 '16 at 02:24
  • 1
    Because the context switch is pretty much done by swapping stacks, and assumes that all threads are waiting inside `switch_thread` and will be changed at the completion of the timer interrupt... no state information is needed by `thread_yield` . The key thing is that the return from the timer tick interrupt will set state to a new thread (if a switch was needed) based on the changed stack returned from `thread_yield`. The `IRET` of the interrupt handler actually restores different state from a different thread stack when it returns, effectively switching threads. – Michael Petch Sep 16 '16 at 02:26
  • 1
    In simplest terms: A timer tick occurs, the current running thread state is saved on the current thread stack. Interrupt handler code runs, sets `yield_on_return` if need be. Just before the interrupt handler is about to exit is switches the stack to a new a thread (different than the stack that that the interrupt actually interrupted). The return from the timer tick interrupt will issue an _IRET_ restoring a different thread context and that is how threads get switched. – Michael Petch Sep 16 '16 at 02:31
  • 1
    If you look at the process of thread creation (`thread_create` function), a thread stack is setup that appears to have been interrupted with all the initial state of the thread, and to make it appear it was executing within the `switch_threads` function. – Michael Petch Sep 16 '16 at 02:48
  • 1
    Earlier I said **All threads (kernel or user space) have 1 stack** - I meant to say that they EACH have 1 stack. – Michael Petch Sep 16 '16 at 02:49
  • 1
    But the absolutely best advice I can offer here is that you load the kernel into _GDB_ and actually attempt to debug a context switch. If you go through instruction by instruction you may get a deeper appreciation for what is going on, and in the process you may self discover why `thread_yield()` needed no parameters. – Michael Petch Sep 16 '16 at 03:24
  • @MichaelPetch, thanks a lot! It turns out that the answer could be just single-word-answer: `IRET`. Now, everything is clear. Please copy the comment to an answer, I am gonna make it accepted. Thanks! :) – Gilgamesz Sep 16 '16 at 19:13
  • 1
    Be my guest at self answering the question.But it is the combination of the _POP_ statements and then the _IRET_ at the end of the interrupt handler. When I wrote that up last night I was very ambiguous looking back on it. But the _IRET_ will be the one responsible in setting CS:EIP to the instruction that will be returned to when the interrupt handler is finished. – Michael Petch Sep 16 '16 at 19:21
  • 1
    Yes, `IRET` is only the part of the switching-context. The very important aspect is a "switching" `esp`. Bascially, it is a crux of the switching task ( here) . But, I highlight `IRET` because that fact was only a lacking puzzle to understand it for me. – Gilgamesz Sep 16 '16 at 19:42

0 Answers0