Hi,
A few comments:
Gizmo wrote: I am considering making the threads make their own stacks, or use no stack.
If each task has a space of (say) 3GB and you place the user stack at the top of task space, you potentially have a 3GB stack. As long as you keep track of where the stack boundaries are, you can dynamically assign stack pages as necessary. I would suggest this is a better method because you are then able to pass initial values to your task. For example, if you want all your processes to start with:
Code: Select all
int main(int argc, char **argv)
{
}
you can just place argc and argv on the stack when you create the new process. You can also provide a return value for tasks which do not exit() nicely. You may want to use this system even if you have a C runtime set up, because you can just pass argc and argv on to the 'real' main function.
Gizmo wrote:
I am going to use software timer interupt driven task switching- each process has a page directory and each process makes threads that can have their own stack. All processes virtual address spaces start at 0 and extend to 4gb.
Are you mapping the kernel space in to each task space? Lots of kernels seem to do this, because it means you do not have to manually switch to kernel space each time a privileged interrupt happens.
Gizmo wrote:
Each process has a structure (127 max processes) containing a id byte, status byte, page directory, bunch of blocks detailing the page location in ram or hardrive, and 127 thread structres
I would suggest having space for more than 127 processes. Hardware task switching limits you to just under 8192 processes and some people consider this an unnecessary limit.
Gizmo wrote:
each thread structure has
id byte
status byte
sleep timer byte
storage for registers
Sounds ok - I would just extend the sleep byte to a longer int. Supposing you are sleeping in mSec intervals, one byte only lets you sleep for half a second. Inevitably when you code it, you will find that the structure needs extending (there's always something you didn't think of
). Storage for registers is often done on the task's ring 0 stack.
Gizmo wrote:
the task switcher does
Code: Select all
loops through process structures
loops through thread structures
thread is ready to execute (see below)
or sleep timer is not 0 yet
subtract 1 from sleep timer
continue looping though threadsuntil you reach 127
continue looping until you reach 127
hlt (idle process, the timer irq handler does task switching)
Thread is ready to execute
set page directory cr3 to the process's pd and load threads registers
return (iret)
Not every thread 'slot' and every task 'slot' will always be filled. You will probably want something like a linked-list which can have a circular reference, so you do not do more checking than you need to. Remember that while you are scheduling the next task, nothing else is happening, so you need to get that next task running as soon as possible. Some people have the scheduler itself running as a separate task.
Gizmo wrote:
When the task switcher is ran if first switches to kernel page directory, saves the registers, sets up its own stack (1 task switcher/interrupt handler stack per cpu to avoid conflicts)
and enters the loop above.
Sounds good, I think.
Gizmo wrote:
Notice how this software task switcher avoids the assumption that task will have stacks, but it does save the registers so it still saves the stack if it exists.
Ah - I think I see what you are getting at now by "No Stack" earlier in your post.
When you get to having user tasks running in ring3 and the kernel in ring0, you
need a ring 0 stack per task. This is because, when you are running in ring 3 and your scheduler interrupt occurs, the CPU will automatically (no intervention) do the following:
* Load ESP0 from the Task State Segment (TSS).
* Simultaneously load CS from the Interrupt Descriptor.
* Push the user ESP3 and SS (I forget which order).
* Push EFLAGS, CS and EIP
* Jump to your ISR.
This means that you need some kind of ring 0 stack. Moving the pushed values to another location simply wastes CPU cycles in a part of your OS which is going to be used a
lot.
Gizmo wrote:
The task switcher is a routine called by the system timer handler and can be called by a software interupt handler (who can be called by the task to implement sleep() ) as well as other software interupt handlers such as io (the thread wants to send io, it first ask the os for a handle to a device, then it can read input at anytime, and if it request write access it can call the interupt who switches the tasks so eventually the device driver task (think microkernel) can process it and return a value into the read stream by the time the requesting task resumes- heh thats long).
Good stuff.
Gizmo wrote:
Anyways this will be my first os so don't assume I know what I am talking about.
Please don't assume I'm trying to pick holes in what you say for the sake of it. Hopefully it will just give you some food for thought.
Cheers,
Adam