Scheduler loophole

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.
Post Reply
LPeter
Member
Member
Posts: 30
Joined: Wed Jan 28, 2015 7:41 am

Scheduler loophole

Post 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?
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Scheduler loophole

Post 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).
LPeter
Member
Member
Posts: 30
Joined: Wed Jan 28, 2015 7:41 am

Re: Scheduler loophole

Post 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)
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Scheduler loophole

Post 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.
LPeter
Member
Member
Posts: 30
Joined: Wed Jan 28, 2015 7:41 am

Re: Scheduler loophole

Post by LPeter »

Right. Should I re-enter user mode every time I switch tasks?
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Scheduler loophole

Post 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.
LPeter
Member
Member
Posts: 30
Joined: Wed Jan 28, 2015 7:41 am

Re: Scheduler loophole

Post 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;
}
LPeter
Member
Member
Posts: 30
Joined: Wed Jan 28, 2015 7:41 am

Re: Scheduler loophole

Post 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(&current_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, &current_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...
Post Reply