Switch a task... and stop IRQs!
Switch a task... and stop IRQs!
Hello! I'm new to this forum and this is my first question about OS Development.
I am very expert at C/C++ Development and a bit at ASM coding
I started coding my own OS about 2 months ago, and I implemented successfully the GDT, the IDT, exceptions, IRQs (timer, keyboard...) and some Paging (just the base...)
Now I'm trying to get work a simple TSS-based Multitasking Environiment: I wrote the "create_task" function which creates a new task starting from its name, entry function, privilege level and priority. For this I use two lists, one for the tasks and one for the TSSs. I use the function "gdt_add_tss" which adds the new TSS to the GDT and returns its 'selector'.
So I create three tasks: one dummy (just for the LTR command), one for the 'init' program and one for a test task, which prints some 'A' on the console screen. Then I use the lcall/ljmp instruction to switch between the tasks in Timer IRQ. The problem is this: my kernel successfully switches to the 'init' function that prints a welcome message and then to my test task that works as well; but then IRQs stop firing!!!
The scheduler stops, the keyboard doesn't respond... it should print the scancode on the screen but no, I can't even reboot the system! >:(
Can someone help me??? I'll post the code if it's necessary. ;D
I am very expert at C/C++ Development and a bit at ASM coding
I started coding my own OS about 2 months ago, and I implemented successfully the GDT, the IDT, exceptions, IRQs (timer, keyboard...) and some Paging (just the base...)
Now I'm trying to get work a simple TSS-based Multitasking Environiment: I wrote the "create_task" function which creates a new task starting from its name, entry function, privilege level and priority. For this I use two lists, one for the tasks and one for the TSSs. I use the function "gdt_add_tss" which adds the new TSS to the GDT and returns its 'selector'.
So I create three tasks: one dummy (just for the LTR command), one for the 'init' program and one for a test task, which prints some 'A' on the console screen. Then I use the lcall/ljmp instruction to switch between the tasks in Timer IRQ. The problem is this: my kernel successfully switches to the 'init' function that prints a welcome message and then to my test task that works as well; but then IRQs stop firing!!!
The scheduler stops, the keyboard doesn't respond... it should print the scancode on the screen but no, I can't even reboot the system! >:(
Can someone help me??? I'll post the code if it's necessary. ;D
Re:Switch a task... and stop IRQs!
I had a problem like this in one of my schedulers. The timer ISR would check for expired quantum, and if so, schedule(). The problem was, I'd send EOI *after* this code, so when there was a scheduling, EOI would never happen, and a new task would start, the PIC never having been sent EOI. So IRQs wouldn't fire anymore. Maybe something similar is going on in your system?
Re:Switch a task... and stop IRQs!
Thanks Ushma, you're right. But now, what can I do? Probably I have to change multitasking mode...Ushma wrote: I had a problem like this in one of my schedulers. The timer ISR would check for expired quantum, and if so, schedule(). The problem was, I'd send EOI *after* this code, so when there was a scheduling, EOI would never happen, and a new task would start, the PIC never having been sent EOI. So IRQs wouldn't fire anymore. Maybe something similar is going on in your system?
What do you think about the stack-swapping method? Is it fast?
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Switch a task... and stop IRQs!
It mainly gets faster when you have many intra-process switches (e.g. switching between two threads of the same address space), and only if you take care of not reloading CR3 when staying in the same address space.falconfx wrote: What do you think about the stack-swapping method? Is it fast?
Otherwise, the performance penalty is not measurable on modern systems, afaik.
Re:Switch a task... and stop IRQs!
I'm sure nothing happens if you reload the cr3 register with the same value y'know.
Re:Switch a task... and stop IRQs!
Don't you use separate page directories/cr3 in every thread? Not because of "really" different address spaces, but only because of different stacks?Pype.Clicker wrote:It mainly gets faster when you have many intra-process switches (e.g. switching between two threads of the same address space), and only if you take care of not reloading CR3 when staying in the same address space.falconfx wrote: What do you think about the stack-swapping method? Is it fast?
Otherwise, the performance penalty is not measurable on modern systems, afaik.
Re:Switch a task... and stop IRQs!
Even if you load the cr3 register with the same value it will flush the caches, example if you would change something in the current page dir and setting the same value for cr3 wouldn't flush the cach you would have a big problem (without using invalidate instruction)...CloudNine wrote: I'm sure nothing happens if you reload the cr3 register with the same value y'know.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Switch a task... and stop IRQs!
No. Each thread will have to receive its own fresh stack and the system is responsible to co-locate all those stacks together. A trivial approach is of course to let the user allocate the future stack space (or at least, give a hint on the maximal stack size) ...bluecode wrote: Don't you use separate page directories/cr3 in every thread? Not because of "really" different address spaces, but only because of different stacks?
but i also plan to allow "expansible stack" by "tunnelling" a stack that has reached its limit to another place ... that's something i definitely need to write a paper on ... it's staying on my mind for too long unused and i fear it'll get garbage-collected soon if i don't share it in a near future ...
Re:Switch a task... and stop IRQs!
You might be clearing the IF flag in eflags when you switch to your other task.
Re:Switch a task... and stop IRQs!
Do you mean by placing a guard page at the top of a stack, and when a thread faults in there, you replace %esp with some newly allocated stack space? Smart...Pype.Clicker wrote: but i also plan to allow "expansible stack" by "tunnelling" a stack that has reached its limit to another place ... that's something i definitely need to write a paper on ... it's staying on my mind for too long unused and i fear it'll get garbage-collected soon if i don't share it in a near future ...
cheers Joe
Re:Switch a task... and stop IRQs!
what do you exactly mean with co-locate? Do you have the same virtual address for the stack in every thread (this would involve switching address space, when switching from thread to thread) or don't you?Pype.Clicker wrote:No. Each thread will have to receive its own fresh stack and the system is responsible to co-locate all those stacks together. A trivial approach is of course to let the user allocate the future stack space (or at least, give a hint on the maximal stack size) ...bluecode wrote: Don't you use separate page directories/cr3 in every thread? Not because of "really" different address spaces, but only because of different stacks?
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Switch a task... and stop IRQs!
@joe: yes. You got the basics of it. In addition to "send ESP to another location", we need to take care that the current stack frame is preserved, for instance, by having the last function's frame mapped in both ancient stack and new stack. That way, any reference taken from the old stack's values are still valid, such as reference to ESP or EBP things (yep, EBP needs to be 'wrapped' too)
@bluecode: when i say "co-locate" i mean they are both living in the same address space, at different virtual addresses. When you switch threads, you just switch to another ESP value. nothing more, nothing less, and they still can damage each other's stack such as they could damage each other's whatever else.
This also means that the usercode has to tell how much space each thread should use as stack, or be happy with the kernel's default value.
@bluecode: when i say "co-locate" i mean they are both living in the same address space, at different virtual addresses. When you switch threads, you just switch to another ESP value. nothing more, nothing less, and they still can damage each other's stack such as they could damage each other's whatever else.
This also means that the usercode has to tell how much space each thread should use as stack, or be happy with the kernel's default value.
`When I use a word,' Humpty Dumpty said, in rather a scornful tone, `it means just what I choose it to mean -- neither more nor less.'
Re:Switch a task... and stop IRQs!
@pype: sorry, but co-locate didn't make any sense to me at first glance and I didn't find it in any dictionary and I've got the good old "Oxford Advanced Learner's Dictionary". Only http://babelfish.altavista.com/ translated "co-locate" into german, but "Co-finden sie" is actually german nonsense ;D . So, I'm really sorry if that question bothered you.
Re:Switch a task... and stop IRQs!
http://en.wikipedia.org/wiki/Co
@Pype: How exactly do you "preserve the stack frame", how would you even determine the size to begin with? ESP and EBP would be configured for the function currently executing which may have just "sub $12, %esp; movl $6, (%esp)" which now requires ESP to be redirected, how do you handle it (especially if -fomit-frame-pointer/-O* was used, or better yet, written in Assembly with only ESP used and EBP being used as an additional general register)?
Whenever something has a co- prefix it usually indicates 2+ things working together. (eg. co-operate)A prefix (form of com-, signifying with, together, in conjunction, joint)
@Pype: How exactly do you "preserve the stack frame", how would you even determine the size to begin with? ESP and EBP would be configured for the function currently executing which may have just "sub $12, %esp; movl $6, (%esp)" which now requires ESP to be redirected, how do you handle it (especially if -fomit-frame-pointer/-O* was used, or better yet, written in Assembly with only ESP used and EBP being used as an additional general register)?
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Switch a task... and stop IRQs!
@bluecode: np. The humty-dumpty quote should have proved you i was in a joking-day mode...
@AR: the 'stack tunnelling' trick of course puts expectation on what code the compiler can generate. A first possible approach is to have in your binary program a collection of "stack size" for your function and resolve the EIP of guard-hitting function to guess what's (in pages) the size of the current stackframe.
Of course, this doesn't work with
My favourite approach remains to make use of EBP and ESP to tell what has to be tunnelled. Whatever beyond ESP is - by definition - unsafe and thus it doesn't need to be tunnelled at all. Keep in mind, too, that i make use of segmentation to tell if there's a stack overflow. So even if you jump over the stack guard page with
you'll be caught by the Stack Fault.
The other thing is that you have to know how deep you should keep things. EBP is a hint, but [ EBP ] is the real stuff. You'll be accessing local variables and arguments with EBP and arguments cannot go further than the previous stackframe's base.
Indeed, ASM-coded and -fomit-stackpointer are showkillers. Probably those programs should tell the OS they don't allow stack tunnelling, or they should manually maintain a "keep_base_stack" pointer that will be used as lowest stack address to be kept.
@AR: the 'stack tunnelling' trick of course puts expectation on what code the compiler can generate. A first possible approach is to have in your binary program a collection of "stack size" for your function and resolve the EIP of guard-hitting function to guess what's (in pages) the size of the current stackframe.
Of course, this doesn't work with
Code: Select all
int myFunc(int some_size)
{
char myData[some_size];
some_code();
}
Code: Select all
void grunt()
{
char fool[8192];
fool[0]=0xdeadbeef; // stack grows downward, so fool[0] is the furthest you can go
}
The other thing is that you have to know how deep you should keep things. EBP is a hint, but [ EBP ] is the real stuff. You'll be accessing local variables and arguments with EBP and arguments cannot go further than the previous stackframe's base.
Indeed, ASM-coded and -fomit-stackpointer are showkillers. Probably those programs should tell the OS they don't allow stack tunnelling, or they should manually maintain a "keep_base_stack" pointer that will be used as lowest stack address to be kept.