Page 1 of 1
Scheduler loophole
Posted: Sun Mar 08, 2015 7:31 am
by LPeter
Hi!
I'm trying to implement multitasking, but I keep finding myself in a loophole.
So I have a process_create method, which simply creates a process with an entry point and a stack. So first what I do is:
Code: Select all
proc_t proc1 = create_process("a");
proc_t proc2 = create_process("b");
That's fine, no actual multitasking or execution. Then I have my scheduler, which has a method called scheduler_switch_task(registers). This is called by the PIT. This methot checks is there are more tasks in the linked list, and based on that it jumps to the next task or the first one.
I also have a scheduler_add_task(proc) which just adds a new process to the linked list.
But when and how should I do process execution (I want to switch to usermode)? If I do it in the scheduler_switch_task, the interrupt will never end. Where ever I would do it, I wouldn't be able to acces any of my code anymore.
Could someone help me?
Re: Scheduler loophole
Posted: Sun Mar 08, 2015 7:43 am
by Nable
LPeter wrote:But when and how should I do process execution (I want to switch to usermode)? If I do it in the scheduler_switch_task, the interrupt will never end.
Initial switch from kernel to user mode is usually done with IRET instruction. You can do the same in the interrupt handler: save state (registers + flags + segment selectors + return address) of interrupted task from registers/stack to some process-specific location, replace them with the the saved state of another task and just do IRET (returning to new task instead of old one).
Re: Scheduler loophole
Posted: Sun Mar 08, 2015 7:54 am
by LPeter
At task switch this gets called:
Code: Select all
void process_execute(process_t * process)
{
int entry_point = process->main_thread->frame.eip;
unsigned int proc_stack = process->main_thread->frame.esp;
cli();
int stack = 0;
__asm__ __volatile__("movl %%esp, %0" : "=r"(stack));
tss_set_stack(0x10, stack);
__asm__ __volatile__(
".intel_syntax noprefix\n"
"mov ax, 0x23\n"
"mov ds, ax\n"
"mov es, ax\n"
"mov fs, ax\n"
"mov gs, ax\n"
"push 0x23\n"
"push %0\n"
"push 0x200\n"
"push 0x1b\n"
"push %1\n"
"iretd\n"
".att_syntax\n"
:
: "r"(proc_stack), "r"(entry_point)
: "ax"
);
}
Which I'm not even sure is right, but if I call this at every task switch, no more interrupts happen anymore. (Of course I save states too)
Re: Scheduler loophole
Posted: Sun Mar 08, 2015 8:08 am
by Nable
It looks definitely wrong. At least, you don't restore EAX's value of the task. Messing with stack from C code is also an unpredictable source of problems.
Re: Scheduler loophole
Posted: Sun Mar 08, 2015 8:12 am
by LPeter
Right. Should I re-enter user mode every time I switch tasks?
Re: Scheduler loophole
Posted: Sun Mar 08, 2015 8:26 am
by Nable
You should return to user mode (or kernel mode, it doesn't matter) each time you return from interrupt.
Whether to switch task (i.e. change saved state of interrupted task) or not is up to scheduler. I mean that returning from interrupt and scheduling are different things and you should separate them:
1. Scheduler (that is invoked by calling some function such as switch_task_if_needed()) just decides whether current task should be changed and modifies the structure where you've saved state of interrupted task in the beginning of interrupt handler.
2. Interrupt handler just handles interrupt, sends EOI to interrupt controller (if needed) and just executes IRETD without thinking whether it will return to the same task or to different one.
Re: Scheduler loophole
Posted: Sun Mar 08, 2015 8:56 am
by LPeter
Is this a good strategy (I'm really beginner)? :
kernel main (pseudo):
Code: Select all
start_multitasking
while (true)
{
if (should_switch)
switch task
should_switch = false;
}
Re: Scheduler loophole
Posted: Sun Mar 08, 2015 10:52 am
by LPeter
[UPDATE]
So now it works, sort of...
What's weird is that:
I have ubuntu linux 64 bit, and if I test it in virtualbox or qemu, I get general protection fault.
My friend also has ubuntu linux and it works for him on bochs, qemu and virtualbox.
What I'm doing now:
in kernel main:
- I load the 2 processes and add them to the list
- I call scheduler_install
In scheduler_install:
- I set the switch_task as the callback for the PIT
- I do this:
Code: Select all
while (true)
{
if (need_switch)
{
process_execute(current_process);
need_switch = false;
}
}
need_switch is set by the switch_task.
My switch_task:
Code: Select all
void switch_task(regs_t * regs)
{
// Copy if usermode
if (regs->cs == 0x1b)
memcpy(¤t_process->frame, regs, sizeof(regs_t));
if (current_process->next_proc == NULL)
{
need_switch = true;
}
else
{
current_process = current_process->next_proc;
}
// Set regs
memcpy(regs, ¤t_thread->frame, sizeof(regs_t));
}
And finally my process_execute:
Code: Select all
void process_execute(process_t * process)
{
int entry_point = process->main_thread->frame.eip;
unsigned int proc_stack = process->main_thread->frame.esp;
cli();
int stack = 0;
__asm__ __volatile__("movl %%esp, %0" : "=r"(stack));
tss_set_stack(0x10, stack);
__asm__ __volatile__(
".intel_syntax noprefix\n"
"mov ax, 0x23\n"
"mov ds, ax\n"
"mov es, ax\n"
"mov fs, ax\n"
"mov gs, ax\n"
"push 0x23\n"
"push %0\n"
"push 0x200\n"
"push 0x1b\n"
"push %1\n"
"iretd\n"
".att_syntax\n"
:
: "r"(proc_stack), "r"(entry_point)
: "ax"
);
}
So I get General Protection fault error code: 44488 and my friend does not. Also I'm sure that this task switching design has a lot of bleeding edges, please tell me if you see one.
Edit: nevermind, it works, I was stupid...