Howto keep the processor idle?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Howto keep the processor idle?

Post by mohammedisam »

Hi there
I have a multitasking kernel. I implemented an idle task to be run whenever there is no other runnable task. The idle task simply goes into an infinite loop and does nothing. The problem is that this vicious cycle of JMP's keeps the processor busy 100% of the time. I tried using the pause instruction in place of the infinite loop, but the result is the same. I can't disable interrupts as I am checking the presence of runnable tasks in the timer interrupt handler. How can I make the processor truly idle?
Thanks in advance :D
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Howto keep the processor idle?

Post by onlyonemac »

You're supposed to be able to use hlt to halt the processor until the next interrupt occurs. If that's still using all (or nearly all) of your CPU then I assume that you're doing something wrong in your task scheduler (for example, you might be getting timer interrupts too frequently and thus keeping the CPU busy even when it's supposed to be idle).
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Re: Howto keep the processor idle?

Post by mohammedisam »

okay. I will try putting HLT instead of my current idle loop and see the result.
thanks!
Boris
Member
Member
Posts: 145
Joined: Sat Nov 07, 2015 3:12 pm

Re: Howto keep the processor idle?

Post by Boris »

Use something like this:

Code: Select all

while(true){hlt(); yield();}
That way if an interrupt handler wakes up a task, your idle task will be replaced with this task.
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Howto keep the processor idle?

Post by onlyonemac »

Boris wrote:Use something like this:

Code: Select all

while(true){hlt(); yield();}
That way if an interrupt handler wakes up a task, your idle task will be replaced with this task.
Except that if his scheduler is pre-emptive then he just needs to hlt every time there are no active tasks. It would be easier to do this:

Code: Select all

void switch_to_next_task()
{
    if (task_waiting())
    {
        switch_to_waiting_task();
    }
    else
    {
        cleanup_and_hlt();
    }
}
(i.e. putting the hlt in the scheduler itself)

If an interrupt other than the timer interrupt wakes a task (such as a keypress, disk I/O, etc.) the interrupt will start the CPU and your interrupt handler can then schedule the task and call switch_to_next_task, which will switch to the task that has been woken.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Re: Howto keep the processor idle?

Post by mohammedisam »

I updated my idle task as such:

Code: Select all

void idle_task_func()
{
loop:
    __asm__ __volatile__("sti\nhlt":::);
    goto loop;
}
It looks like the CPU is still being busy. I think my timer handler is the culprit and it needs to be more lightweight. Here is the handler:

Code: Select all

void _timer_handler(regs_t r)
{
  __asm__ __volatile__("cli":::);
  _ticks++;
  /* we are using 100Hz frequency, so update seconds every 100 ticks */
  if((_ticks%100) == 0) { _secs++; }
  
  task_t *current = get_current_task();
  /* called before tasking is init-ed */
  if(!current) goto finish;
  
  if(current == idle_task && get_next_runnable() == idle_task) goto finish;
  
  /* current task is sleeping or blocked? Schedule */
  if(current->state != TASK_RUNNING) { scheduler(&r); goto finish; }
  
  if(current->time_left) current->time_left--;
  else { scheduler(&r); goto finish; }
  
finish:
  pic_send_eoi(0);
}
Any suggestions?

Cheers
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Howto keep the processor idle?

Post by onlyonemac »

mohammedisam wrote:

Code: Select all

if((_ticks%100) == 0) { _secs++; }
I believe modulus to be a somewhat slow operation and you could replace instance where _secs is used with a call to get_secs(), which returns _ticks / 100.
mohammedisam wrote:

Code: Select all

get_current_task();
mohammedisam wrote:

Code: Select all

get_next_runnable()
How complex are these functions? Bear in mind that the former especially is being called on every timer tick.
mohammedisam wrote:

Code: Select all

if(!current) goto finish;
If I'm understanding your code correctly, that line is to prevent a crash if your scheduler isn't initialised yet. In that case, remove the line and only enable the timer once your scheduler has been initialised, otherwise you're adding an extra conditional test on every timer tick.
mohammedisam wrote:

Code: Select all

if(current == idle_task && get_next_runnable() == idle_task) goto finish;
Sorry but I don't understand this line. Is this supposed to call the idle task when there's nothing to schedule? (Because it doesn't.)
mohammedisam wrote:

Code: Select all

if(current->time_left) current->time_left--;
This line can only mean that you're scheduling each task for more than one tick. What is current->time_left initialised to? Because that's how many ticks you're going to process before another task gets scheduled, and if that's too high for your idle task then your idle task isn't going to keep the CPU very idle.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Howto keep the processor idle?

Post by gerryg400 »

mohammedisam wrote:It looks like the CPU is still being busy
How do you know that it's busy?
If a trainstation is where trains stop, what is a workstation ?
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Howto keep the processor idle?

Post by onlyonemac »

gerryg400 wrote:
mohammedisam wrote:It looks like the CPU is still being busy
How do you know that it's busy?
To be honest I've been wondering that all along. Oh, and bear in mind that if you're focussing on CPU clock speed or something you'll probably have to perform some ACPI initialisation/configuration before CPU speed throttling will take effect.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Re: Howto keep the processor idle?

Post by mohammedisam »

onlyonemac wrote: I believe modulus to be a somewhat slow operation and you could replace instance where _secs is used with a call to get_secs(), which returns _ticks / 100.
It's worth a try.

The code for get_current_task() is one line, returning a pointer to the current task's struct:

Code: Select all

task_t *get_current_task()
{
  return _task;
}
It is merely a convenience function. The code for get_next_runnable() is:

Code: Select all

task_t *get_next_runnable()
{
    task_t *next = ready_queue;
    /* Running queue is empty? run idle task */
    if(!next) next = idle_task;
    return next;
}
where read_queue is a linked list containing the ready-to-run processes.
onlyonemac wrote:

Code: Select all

if(!current) goto finish;
If I'm understanding your code correctly, that line is to prevent a crash if your scheduler isn't initialised yet. In that case, remove the line and only enable the timer once your scheduler has been initialised.
Hmmm. Sounds quite reasonable. I guess I actually don't need the timer before tasking is init'ed.

Code: Select all

if(current == idle_task && get_next_runnable() == idle_task) goto finish;
This was a desperate move, I was trying to check if there is no runnable task other than idle, so as to bypass the rest of the handler function and head out. It turned out it added nothing and still the processor is busy.
onlyonemac wrote: What is current->time_left initialised to? Because that's how many ticks you're going to process before another task gets scheduled, and if that's too high for your idle task then your idle task isn't going to keep the CPU very idle.
Here is how I initialize timeslices for new processes (in order of timer ticks):

Code: Select all

void reset_task_timeslice(task_t *task)
{
  switch(task->priority)
  {
    case PRIO_NORMAL: task->time_left = 20; break;
    case PRIO_LO:     task->time_left = 10; break;
    case PRIO_HI:     task->time_left = 50; break;
    case PRIO_IDLE:   task->time_left =  1; break;
    case PRIO_ZOMBIE: task->time_left =  0; break;
    default:          task->time_left = 20; break;
  }
}
The timeslice for the idle process is only 1, this shouldn't keep the processor busy, right?. But on the other hand, this means the scheduler will be called on each and every timer tick (if the idle task is the running task). This will add to the overhead of the timer handler and compound my problem further, right?
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Re: Howto keep the processor idle?

Post by mohammedisam »

gerryg400 wrote:
mohammedisam wrote:It looks like the CPU is still being busy
How do you know that it's busy?
I am testing my kernel using Bochs under Fedora Linux. The GNOME System Monitor shows that at any given time (when my kernel is active) one of my quad processors is running at 100%. When I pause Bochs, the activity returns to its baseline (3-4%). If it runs for a while, all my other programs would considerably slow down, so I have to test my kernel after I close other windows.
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Howto keep the processor idle?

Post by onlyonemac »

mohammedisam wrote:I am testing my kernel using Bochs under Fedora Linux. The GNOME System Monitor shows that at any given time (when my kernel is active) one of my quad processors is running at 100%.
Most emulators, especially Bochs, will use all available CPU power even when the emulated CPU is supposed to be idle. I'm afraid I can't suggest a better way of measuring CPU usage as I'm not too familiar with it myself.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Howto keep the processor idle?

Post by onlyonemac »

mohammedisam wrote:The code for get_current_task() is one line, returning a pointer to the current task's struct:

Code: Select all

task_t *get_current_task()
{
  return _task;
}
It is merely a convenience function. The code for get_next_runnable() is:

Code: Select all

task_t *get_next_runnable()
{
    task_t *next = ready_queue;
    /* Running queue is empty? run idle task */
    if(!next) next = idle_task;
    return next;
}
where read_queue is a linked list containing the ready-to-run processes.
You should probably make those functions inline (instead of declaring "task_t *get_next_runnable()", declare "inline task_t *get_next_runnable()"). You might even want to think about removing the first one (although keeping it as a separate function does make the code easier to understand).
mohammedisam wrote:The timeslice for the idle process is only 1, this shouldn't keep the processor busy, right?. But on the other hand, this means the scheduler will be called on each and every timer tick (if the idle task is the running task). This will add to the overhead of the timer handler and compound my problem further, right?
You probably want to keep that timeslice as short as possible, otherwise the idle task will keep running even when something else is ready to run. However, you're still getting timer interrupts 100 times every second no matter which way you're looking at it. When I first commented on that line, I didn't realise that your scheduler had priorities, sorry.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
Techel
Member
Member
Posts: 215
Joined: Fri Jan 30, 2015 4:57 pm
Location: Germany
Contact:

Re: Howto keep the processor idle?

Post by Techel »

Sleeping ~10ms is pretty much. Declaring stuff as inline etc is premature optimization in my opinion, the code is just a few calls here and there. Did you enable realtime sync in bochs?
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Re: Howto keep the processor idle?

Post by mohammedisam »

onlyonemac wrote:Most emulators, especially Bochs, will use all available CPU power even when the emulated CPU is supposed to be idle.
Looks like I will have to live with it. I cannot risk testing it on physical device yet.
Post Reply