Page 1 of 3
I need more about creating a task/process
Posted: Fri Aug 12, 2005 6:46 am
by calpis
Hi, I read alot about how to start multitasking but, I can't find anything about implementing a simple tasks. All I am aiming to do is to create 2 tasks and see how the whole mulititasking work.
I can only understand what the task control block and task table are for. But what i don't understand is what's inside a task(such as stack, new page directory and tables, size of stack).
I am trying to implement the multitasking for about a month now, still I am stuck at the begining stage. Here is what I think I know:
I need a tss descriptor in gdt for initialize a hardware switch.
I need a task table to hold the task control blocks.
Can anyone help me please.
I am looking at a diagram from intel. a system architecture overview.
In a task state segement(TSS), a task has code, data, and stack.
What I want to know is
what does the stack stores???
what does the code stores? is it just a cs register from the task below?
What does the data stores? is it just a ds register from the task below?
what does a new page directory and table do when create a task?
Here is my task state segment or task content, whatever the name is...
Code: Select all
struct task_state_segment
{
short backlink, __blh;
int esp0;
short ss0, __ss0h;
int esp1;
short ss1, __ss1h;
int esp2;
short ss2, __ss2h;
int cr3;
int eip;
int eflags;
int eax, ecx, edx, ebx;
int esp, ebp, esi, edi;
short es, __esh;
short cs, __csh;
short ss, __ssh;
short ds, __dsh;
short fs, __fsh;
short gs, __gsh;
short ldt, __ldth;
short trace, bitmap;
}__attribute__((packed));
Sorry if my post is a bit of a mass, coz thats the state of brain I am at at the moment~! Thanks for any help and advice~
Re:I need more about creating a task/process
Posted: Fri Aug 12, 2005 6:56 am
by Pype.Clicker
okay, let's try to make it simple ...
there are two things that come into play: threads and address spaces. For a first try, i suggest you forget about address spaces (that basically means that any of your "tasks" will have the same content for CR3 and LDT, and usually the same segment registers aswell).
The stack is of course used to store local state for the current computation (e.g. function call chains, arguments, local variables and the like). The stack state (SS:ESP) is the minimal thing you have to save in order to do a switch -- in other word, while in 'all threads in the same address space', the only thing your task control block need to store is that SS:ESP.
Re:I need more about creating a task/process
Posted: Fri Aug 12, 2005 7:10 am
by calpis
So do you mean the stack stores ebp, esp, esi, eax,ebx....(unsigned) func...etc on the stack in a kind of specified order which you refer as local state? And all I need is a (SS:ESP) to do a switch
And then go and use a scheduler to switch around the (SS:ESP) between two task?
Thanks for your help.
Re:I need more about creating a task/process
Posted: Fri Aug 12, 2005 8:00 am
by JoeKayzA
Basically, it seems you got it right.
You should, however, still keep a seperate structure for each thread, that holds the corresponding ss:esp, as well as a thread 'status' (running/blocked/waiting...) and the page directory. For storing the cpu status, the stack comes in handy because you can simply push the registers on it and restore them during an interrupt.
EDIT: Storing the page directory is of course only needed when you use multiple address space, which is, as Pype mentioned, not neccessary for the beginning.
cheers Joe
Re:I need more about creating a task/process
Posted: Fri Aug 12, 2005 9:04 am
by Pype.Clicker
chaisu chase wrote:
So do you mean the stack stores ebp, esp, esi, eax,ebx....(unsigned) func...etc on the stack in a kind of specified order which you refer as local state? And all I need is a (SS:ESP) to do a switch
No. The stacks only stores what you push on it. So if you want it to store the local state, you need to use things like (assuming fs/gs isn't used, and that all the threads use the same stack segment, only changing the stack pointer)
Code: Select all
do_switch:
pusha ;;; save the cpu state on the stack
push ds
push es ;;; just to keep it sane
pushf ;;; keep the status register aswell
cli ;; so that you don't get interrupted
mov [outgoing_task],esp
mov esp,[incoming_task]
popf ;;; retrieve 'incoming task's state
pop es
pop ds
popa
ret
Really, if that doesn't sound clear, i suggest you get an assembly book and read through the 'stack' chapter: that's a crucial element of system programming and you won't get far without it.
Re:I need more about creating a task/process
Posted: Sat Aug 13, 2005 7:15 pm
by calpis
hi, I have tried to implement the task. But it seems to be not working.
my kernel is based around bran's dev kernel. I use the c method to install a task interrupts. and added the asm code for the task irq handler. I also have a method to create task. but again, I am not sure if this whole thing is correct.
I don't know what is wrong, the kernel runs like normal. But its doesn't print what I want. I am really just testing and see it the switcher works.
Please tell me whats missing or wrong. Many Thanks~
My asm for task switch
Code: Select all
extern current_task
extern kernel_task
extern task_schedule_handler
extern tss
irq_task_stub:
pusha
push ds
push es
push fs
push gs
mov eax, [current_task]
mov [eax], esp
mov eax, [kernel_task]
mov esp, [eax]
mov eax, task_schedule_handler;;irq_handler
call eax
mov eax, [tss]
add eax, 4
mov [eax], esp
mov eax, [kernel_task]
mov [eax], esp
mov eax, [current_task]
mov esp, [eax]
pop es
pop ds
pop fs
pop gs
popa
add esp, 8
iret;;ret
This is what I have in task.c which includes task handler installer.
Code: Select all
unsigned int create_task(void (*function)(), unsigned int priority)//, priority)
{
//store all the regs into task_state_info by save reg
struct task_control_block *this_task = (struct task_control_block *)page_allocator(sizeof(struct task_control_block));
this_task[0].task_esp = getesp();
this_task[0].task_time = 10;
this_task[0].task_cr3 = first_page_directory;
this_task[0].task_priority = priority;
//this_task[0].task_id = ;
this_task[0].tasks = (struct task_control_block *)page_allocator(sizeof(struct task_state));
this_task[0].tasks->eax = 0;
this_task[0].tasks->ebx = 0;
this_task[0].tasks->ecx = 0;
this_task[0].tasks->edx = 0;
this_task[0].tasks->edi = 0;
this_task[0].tasks->esi = 0;
this_task[0].tasks->cs = 0x8;
this_task[0].tasks->eflags = 0x202;
this_task[0].tasks->eip = (unsigned)function;
this_task[0].tasks->esp = (unsigned int)this_task[0].tasks;
setcr3(this_task[0].task_cr3);
return (unsigned int) this_task;
}
void task_schedule_handler(struct regs *r)
{
if(r->int_no == 32)
{
kprintf("\nHello from timer\n");
outportb(0x20,0x20);
}else if (r->int_no ==34)
{
kprintf("\nHello from scheduler\n");
outportb(0x20,0x20);
}
}
void task_schedule_install()
{
irq_install_handler(2, task_schedule_handler);
}
This is what I have in the kernel main.
Code: Select all
unsigned int simpletask1()
{
int i;
for (i = 0; i < 1000000; i++)
{
kprintf(" simple task %d\n",i);
}
}
.
.
.
task_schedule_install(); //install irq handler for task scheduler
kprintf(" Task Initialized \n");
unsigned int *task_queue;
unsigned int num_of_task =0;
struct task_control_block *kernel = (struct task_control_block *)page_allocator(sizeof(struct task_control_block));
kernel[0].task_esp = getesp();
kernel[0].task_cr3 = first_page_directory;
kernel[0].task_id = 0;
kernel[0].task_time = 0;
task_queue[0] = (unsigned int) kernel;
current_task = kernel;
kernel_task = kernel;
struct task_control_block *task1 = (struct task_control_block *)create_task(simpletask1,255);
task_queue[1] = (unsigned int) task1;
struct task_control_block *task2 = (struct task_control_block *)create_task(simpletask2,255);
task_queue[2] = (unsigned int) task2;
Re:I need more about creating a task/process
Posted: Sun Aug 14, 2005 8:39 am
by calpis
Hi, I have changed my code and just forget about using the interrupt. Instead, I use the timer instead. Which called the contentt_switch_stub from there. Look as tho it is switching now~
Thanks anyway~
Re:I need more about creating a task/process
Posted: Sun Aug 14, 2005 6:29 pm
by calpis
hi, can anyone tell me what this mean?
I used the do_switch asm from pype's code and bochs got freezen~!
And give the following in bochs log.
Code: Select all
00015117148e[CPU ] load_seg_reg: GDT: ES: index(1800) > limit(00001f)
00029446500p[MGUI ] >>PANIC<< POWER button turned off.
00029446500i[SYS ] Last time is 1124065649
00029446500i[CPU ] protected mode
00029446500i[CPU ] CS.d_b = 32 bit
00029446500i[CPU ] SS.d_b = 32 bit
00029446500i[CPU ] | EAX=00000007 EBX=0000000a ECX=00000000 EDX=000003d5
00029446500i[CPU ] | ESP=0012cfa8 EBP=00104b50 ESI=0002cb46 EDI=001055d8
00029446500i[CPU ] | IOPL=0 NV UP DI PL NZ NA PO NC
00029446500i[CPU ] | SEG selector base limit G D
00029446500i[CPU ] | SEG sltr(index|ti|rpl) base limit G D
00029446500i[CPU ] | DS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00029446500i[CPU ] | ES:0010( 0002| 0| 0) 00000000 000fffff 1 1
00029446500i[CPU ] | FS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00029446500i[CPU ] | GS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00029446500i[CPU ] | SS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00029446500i[CPU ] | CS:0008( 0001| 0| 0) 00000000 000fffff 1 1
00029446500i[CPU ] | EIP=00100e03 (00100e03)
00029446500i[CPU ] | CR0=0xe0000011 CR1=0x00000000 CR2=0x00000000
00029446500i[CPU ] | CR3=0x0009c000 CR4=0x00000000
00029446500i[ ] restoring default signal behavior
00029446500i[CTRL ] quit_sim called with exit code 1
Thanks in advanced.
Re:I need more about creating a task/process
Posted: Sun Aug 14, 2005 8:30 pm
by AR
That says you attempted to load ES with 1800th descriptor which is outside the GDT's limit.
Re:I need more about creating a task/process
Posted: Mon Aug 15, 2005 9:38 am
by calpis
Any other how to fix it? All I know its that it happened when I change my content_switch_code to this. Thanks for your reply.
Code: Select all
extern in_task
extern out_task
extern tss
extern stub_test
global content_switch_stub
content_switch_stub:
pusha
push ds
push es
push fs
push gs
;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushf
;;cli
mov [out_task],esp
mov esp, [in_task]
mov eax,[tss]
add eax, 4
popf
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;mov eax, [in_task]
;;mov [eax], esp
;;mov eax, [out_task]
;;mov esp, [eax]
;;mov eax, [tss]
;;add eax, 4
;;mov [eax], esp
;;mov eax, [out_task]
;;mov [eax], esp
;;mov eax, [in_task]
;;mov esp, [eax]
pop gs
pop fs
pop es ;;problem
pop ds
popa
;;call stub_test ;;from timer.c
;;add esp, 8
ret ;;problem
Re:I need more about creating a task/process
Posted: Mon Aug 15, 2005 6:24 pm
by Brendan
Hi,
The very first time a newly created thread does a thread switch, it will skip the start of your thread switch code and begin running in the middle (without pushing values on it's stack first). To avoid problems you'd need to create the new thread's stack with everything "pre-pushed" onto it, so that during the first thread switch these values can be popped off.
I think you're either not doing this, or not doing it correctly.
For example, when you create a new thread you need to "emulate" the following (based on the thread switch code you posted):
- push EIP (the near return address)
pusha
push ds
push es
push fs
push gs
pushf
Cheers,
Brendan
Re:I need more about creating a task/process
Posted: Mon Aug 15, 2005 6:43 pm
by calpis
May be I am not doing it correctly, because I don't know what to do.
I create the first and the rest of tasks using the same method. Did you mean I must initializes the first tasks first with all the regs prepushed to (which stack?)
can you tell me what is "the near return address"? And what should do to get use switching task.
I don't really get the push EIP method.
I m pretty messed up with my code at the moment~ Many thanks for your help.
P.S. Could anyone tell me what it means please.
Code: Select all
00004456835i[CPU ] can_push(): expand-up: esp < N
00004456835i[CPU ] push_32(): push outside stack limits
00004456835i[CPU ] can_push(): expand-up: esp < N
00004456835i[CPU ] can_push(): expand-up: esp < N
00004456835e[CPU ] exception(): 3rd (12) exception with no resolution,
Re:I need more about creating a task/process
Posted: Mon Aug 15, 2005 10:13 pm
by Brendan
Hi,
Let's start with very simple thread switch code:
Code: Select all
;Switch threads
;
;Input
; eax = address where next thread's ESP is stored
switchThreads:
;Save the previous thread's state
pusha
mov ebx,[address_of_last_threads_ESP]
mov [ebx],esp
;Change to next thread's stack
mov [address_of_last_threads_ESP],eax
mov esp,eax
;Load the next thread's state
popa
ret
As long as EAX is set correctly before this code is called it will work (note: it won't change segment registers or anything and it will cause problems if any of the threads use CPL=3, but it's meant to be as simple as possible).
When your OS is initializing you'd want to create scheduler data for the first thread, which is the initialization code itself. For this example the thread switch code is so simple that this can hopefully be ignored for now.
During the very first task switch where you switch from the OS's "initialization thread" to a newly created thread, the initialization thread would run the "Save the previous thread's state" part of the thread switch code, then you change stacks, and after this you load the new thread's state from the new thread's stack. Here's the problem - the new thread's state must be on it's stack so that it can be loaded by the thread switch code.
To create a new thread you'd need to create the scheduler data for the new thread, and initialize the new thread's stack so that it contains the "state" that the thread switch code is going to pop off the new thread's stack. In this simple case it could be something like:
Code: Select all
;Spawn a new thread
;
;Input
; eax = address of new thread's code to run
spawnThread:
pusha
pushf
<setup some scheduler data>
;Switch to the new thread's stack
mov ebp,esp
cli
mov esp,<the address for the top of the new thread's kernel stack>
;Store the state data that the thread switch code will try to get from the stack
push eax ;EIP = address of new thread's code to run
pusha
;Restore the original stack
mov esp,ebp
;Return to caller
popf
popa
ret
For your old code, the thread switch routine tried to pop a heap of things from the stack that (as far as I can tell) were never put on the stack to begin with.
chaisu chase wrote:P.S. Could anyone tell me what it means please.
Code: Select all
00004456835i[CPU ] can_push(): expand-up: esp < N
00004456835i[CPU ] push_32(): push outside stack limits
00004456835i[CPU ] can_push(): expand-up: esp < N
00004456835i[CPU ] can_push(): expand-up: esp < N
00004456835e[CPU ] exception(): 3rd (12) exception with no resolution,
Exception 12 is a "stack fault", and is caused when you haven't got a valid stack or ESP is outside of the segment limits. In this case the CPU tries to start the "stack fault" exception handler but can't push the return information onto the stack, so it triple faults.
Cheers,
Brendan
Re:I need more about creating a task/process
Posted: Thu Aug 18, 2005 11:06 am
by calpis
hi, thanks for your reply,
I have tried the method you posted...but I think, I don't quite understand how the context are saved to the task stack or task_state in my tcb. May be I think I don't know how the assembly code works.
But I tried to use some emulator to help me understand it. However, its a still confuse.
I need to know what you mean from the last post.
In the spawnThread:
<setup some scheduler data>
What sort of scheduler data did you mean? Can you tell abit in details please.
Also:
Code: Select all
mov esp,<the address for the top of the new thread's kernel stack>
;Store the state data that the thread switch code will try to get from the stack
push eax ;EIP = address of new thread's code to run
pusha
;Restore the original stack
mov esp,ebp
;Return to caller
popf
popa
ret
Restore original stack... that I just don't get.
The "address for the top of the new thread's kernel stack" its exactly the address of the new process stack, the &stack.
In each create process method, we always have some preset data(in blueIllusion multitasking example). we put
0x0202;
0x08;
(unsigned int)function;
0;
0;
0;
.
.
.
0x10;
So all the create process will have these preset data, when we switch, we are switching the these data with the current context of the register everytime, and restore them. I just don't see how...I guess I am really confuse or I have missed something.
I have looked at afew examples already, I now understand each process must have a stack of its own.
But it is the switch code or the assembly code that I just don't get.
I really wanted to understand and get this done.
Please feed me with more details.
Thanks in advance.
Re:I need more about creating a task/process
Posted: Fri Aug 19, 2005 6:45 am
by calpis
Hi, I have manage to understand how the switch works.
Now I have test my scheduler and the queue. It works fine. But I don't actually swap the two task yet. I do it by pressing key
Code: Select all
My context switch code ends with ret that gives me a reboot
00601919001e[CPU ] seg = DS
00601919001e[CPU ] seg->selector.value = 0000
00601919001e[CPU ] write_virtual_checks: valid bit = 0
00601919001e[CPU ] CS: 0008
00601919001e[CPU ] IP: 1f3c
00601965429p[CPU ] >>PANIC<< prefetch: running in bogus memory
Code: Select all
My context switch code ends with iret that gives me a reboot
00007004380p[CPU ] >>PANIC<< iret: return CS selector null
Code: Select all
mov [current_task_esp],esp
call scheduler_handler
;;All my scheduler code did was to make the
;;current_task_esp = the next task[0].task_esp
mov esp, [current_task_esp]
do I also need to do something about the CS selector? I tried to follow the legend os tutorial as much as possible.
Thanks in advacnce