Multitasking Issues
Multitasking Issues
Can anybody point me in the right direction for supporting multitasking in my kernel? I've been reading Intel manuals and searching a lot but unfortunately, have not found a tutorial that tells you what and why. I'd really appreciate it if somebody could give a link to a tutorial or something. Thanks in advance.
P.S: I've read wiki's pages but I'm still confused about TSS and etc.
P.S: I've read wiki's pages but I'm still confused about TSS and etc.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
first of all, sorry for my poor english.
to create a multitasking system, you have only two " 2 " choices
software and hardware taskswitching, software you do by change the
stack pointer, first you setup a stack layout like when a exception occurs.
and on that kinda of multitasking to use protection you have only one TSS
loaded be ltr(desc_in_GDT);
on each taskswitching on your scheduler you have to change the
TSS.SS0, TSS.ESP0 from your next_task_to_be_switched.
the other one kinda of multitasking is hardware taskswitching, but isn´t
used anymore in that new 64 processor in long mode.
but if you want to use it in 32 bits mode, go ahead, it is much slow then
software taskswitching.
on that kinda of taskswitching you have to do a jump_to_tss_in_GDT
in case you want, I have my kernel right now in software taskswitching
much used in other computer archtecture, RISC, MACINTOSH, SPARC,
ETC.
using software taskswitching is much faster.
just email me, then I can send to you my sources.
[email protected]
to create a multitasking system, you have only two " 2 " choices
software and hardware taskswitching, software you do by change the
stack pointer, first you setup a stack layout like when a exception occurs.
and on that kinda of multitasking to use protection you have only one TSS
loaded be ltr(desc_in_GDT);
on each taskswitching on your scheduler you have to change the
TSS.SS0, TSS.ESP0 from your next_task_to_be_switched.
the other one kinda of multitasking is hardware taskswitching, but isn´t
used anymore in that new 64 processor in long mode.
but if you want to use it in 32 bits mode, go ahead, it is much slow then
software taskswitching.
on that kinda of taskswitching you have to do a jump_to_tss_in_GDT
in case you want, I have my kernel right now in software taskswitching
much used in other computer archtecture, RISC, MACINTOSH, SPARC,
ETC.
using software taskswitching is much faster.
just email me, then I can send to you my sources.
[email protected]
Right. Software taskswitching just requires swapping out all the registers (including IP and stack) with that of another task every so often (often done using a timer interrupt, though in cooperative multitasking there is a yield function that does that). Hardware multitasking does roughly the same thing, and requires no less work -- about the only positive I can think of is that it provides a structure for multitasking that's rather standardized among ia32 processors, but on the other hand it's much harder to work with and understand, and has the tendency to kill the whole system on a little fudge, whereas with software multithreading you can build in support for special cases and such.
Software task switching is really quite easy though. I'd be willing to write example code for it, but probably loads exists on the web anyway
Software task switching is really quite easy though. I'd be willing to write example code for it, but probably loads exists on the web anyway
Here is some stuff from Bona Fide ( http://www.osdever.net )
http://www.osdever.net/tutorials/multitasking.php
http://www.osdever.net/tutorials/soft_ts.php
http://www.osdever.net/tutorials/multitasking.php
http://www.osdever.net/tutorials/soft_ts.php
Thank you so much everyone. I'm starting to get quite a better picture of what I should do and what I shouldn't. Alright let's say that I want to implement preemptive multitasking and I am going to do it by means of software. Now every process should have its own Code, Data and stack segment but let say IRQ0 is fired and my scheduler is trying to pass the control to a process's thread. Now if I change the CS to another segment selector that I will create for that process on the fly, the IRQ0 will not be able to be handled by my kernel anymore because CS is changed. What should I do then? should I just use CS that I am using for my kernel? That would be neither rational nor safe, right? Thank you again everyone.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
What you need to do is to have the interrupt handler code available at the same address in each processes address space. Then, by switching the value of ESP and EIP stored on the stack (ESP is only stored if the privilege level changes) you effect the task switch. Most people use paging and map the kernel's pages (which presumably include the task switch interrupt handler) at the same location in every process. You then just change the page directory base register for each task switch and the interrupt handler continues as it was, because the handler is still in the same location.
Regards,
John.
Regards,
John.
Alright, let me see if I got it right. suppose I am not using Paging and all addresses are physical. Now what I should do is to create the scheduler in the kernel. A process can be created with a procedure or a function that I should code. A process has 1 main thread. Each of the processes have to have their own segments and etc. When a process is created, the scheduler should check for its priority and other settings and then transfer the control to that process while having saved kernel's segments in process's stack.
There are certain points that I really can't figure out about this scheme:
1) How am I supposed to handle IRQs in processes while I should not have control over what code is written for them?
2) Suppose a process is running at the privilege level of 3. How is the kernel's scheduler supposed to control it while all segment registers are changed?
I'd really appreciate it if somebody could clear these points for me. By the way, than you everyone for your responses.
There are certain points that I really can't figure out about this scheme:
1) How am I supposed to handle IRQs in processes while I should not have control over what code is written for them?
2) Suppose a process is running at the privilege level of 3. How is the kernel's scheduler supposed to control it while all segment registers are changed?
I'd really appreciate it if somebody could clear these points for me. By the way, than you everyone for your responses.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
-
- Member
- Posts: 2566
- Joined: Sun Jan 14, 2007 9:15 pm
- Libera.chat IRC: miselin
- Location: Sydney, Australia (I come from a land down under!)
- Contact:
You generally don't change it during the IRQ, but at the end. For instance, if your IRQ handler gets passed a pointer to a structure of registers (taken from the IRQ stack) then you can change the values there and then the structure's new data will be popped off the stack. That's what I do, though it doesn't really work and I've opted to remove multitasking from my OS.
Alright. I finally implemented a very basic scheduler but I still seem to have problems with context switching. The problem is that I don't understand whether I should use TSS for software context switching or not? I have not yet implemented paging yet so every memory location is physical (for now). I'm just really confused now so if any of you could, please, give me a general idea on how this should be done, it would be great. I mean should I use call gates? interrupts? who am I? who are you?
I really need a source that describes how CPL, DPL, RPL and other things should be changed, how I should move from DPL 0 to DPL 3 and vice versa and etc. I'd really appreciate it if somebody could explain these for me or could point me to a tutorial of some type.
Thanks in advance.
I really need a source that describes how CPL, DPL, RPL and other things should be changed, how I should move from DPL 0 to DPL 3 and vice versa and etc. I'd really appreciate it if somebody could explain these for me or could point me to a tutorial of some type.
Thanks in advance.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
Call Gates are used if you want to jump to a code with higher priority (that is DPL < CPL). Think of the CPL-3 as a big man (very big man). He can't cross on the other side of the bridge (that is the DPL-0), because he's too big to cross it, without crashing it. The call gate is just like a bigger and stronger bridge, so the big man can cross it succesfully.XCHG wrote:Alright. I finally implemented a very basic scheduler but I still seem to have problems with context switching. The problem is that I don't understand whether I should use TSS for software context switching or not? I have not yet implemented paging yet so every memory location is physical (for now). I'm just really confused now so if any of you could, please, give me a general idea on how this should be done, it would be great. I mean should I use call gates? interrupts? who am I? who are you?
I really need a source that describes how CPL, DPL, RPL and other things should be changed, how I should move from DPL 0 to DPL 3 and vice versa and etc. I'd really appreciate it if somebody could explain these for me or could point me to a tutorial of some type.
Thanks in advance.
More about context-switching here: http://en.wikipedia.org/wiki/Context_switch
More about system calls here:
http://en.wikipedia.org/wiki/System_call
I think, I have problems with Bochs. The biggest one: Bochs hates me!
If you are using software multitasking, then all context switches will be done using the iret instruction. You put the correct data on the stack and then iret to the next process. You will only need one TSS when you start doing CPL0 -> CP3 switches because on the return to CPL0 the proccessor will read the CPL0 stack pointer and stack segment.
these are some tutorials that helped me greatly.
http://www.osdever.net/tutorials/multitasking.php and
http://www.osdever.net/tutorials/soft_ts.php
If you have more questions, feel free to ask them. I can even give code that will successfully do CP0 -> CP3 and CP0 -> CP0 context switches.
these are some tutorials that helped me greatly.
http://www.osdever.net/tutorials/multitasking.php and
http://www.osdever.net/tutorials/soft_ts.php
If you have more questions, feel free to ask them. I can even give code that will successfully do CP0 -> CP3 and CP0 -> CP0 context switches.
I created a Task State Segment. Then I started coding one simple routine that sets the GDT segment descriptor for the TSS, sets the SS0 field of the TSS and then uses the LTS instruction but I get the below error right on the LTS instruction:
Below is the code that I have written. I have hardcoded some of the parts of the code to make it readable. I know that the 32nd byte of my GDT (for 8 bytes) is free.
Thank you guys in advance
Code: Select all
00002963366e[CPU0 ] LTR: doesn't point to an available TSS descriptor!
Code: Select all
XOR EBX , EBX
XOR ECX , ECX
; ECX:EBX = GDT Descriptor Table for TSS
; Low Order DWORD
MOV EBX , OFFSET TSS_STRUCT
SHL EBX , 0x00000010
OR EBX , 0x00000067
; High Order DWORD
MOV EAX , OFFSET TSS_STRUCT
MOV EDX , EAX
SHR EDX , 0x00000010
AND EDX , 0x000000FF
AND EAX , 0xFF000000
OR EAX , EDX
OR EAX , 0x00408900
MOV EBX , EAX
MOV DWORD PTR [OFFSET GDT + 32] , EBX
MOV DWORD PTR [OFFSET GDT + 36] , ECX
LEA EDI , [TSS_STRUCT]
MOV WORD PTR [EDI + 8] , SS
MOV EAX , 32
LTR AX
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
I finally created the TSS. I have also made a simple scheduler that can pick processes depending on their priorities from my process queue. The problem is that I can't really switch to PVL 3 processes. I have created a structure for each process that is 128 bytes long. I have attached the structure of each process to this thread.
I am picking processes out of process queue, in IRQ0 handler, where my scheduler resides. Now for testing, I just got the CS:EIP of one of the processes that my scheduler picks and then I did an IRET. I get the below error as soon as I do that:
This is the code for the IRET:
EDI points to the currently picked task's structure (attached to this post). Can somebody tell me what I am doing wrong? Isn't it possible to jump from CPL 0 to CPL 3? Note that I am not changing DS/ES/SS/FS/GS for the new process.
Any help would be really appreciated.
I am picking processes out of process queue, in IRQ0 handler, where my scheduler resides. Now for testing, I just got the CS:EIP of one of the processes that my scheduler picks and then I did an IRET. I get the below error as soon as I do that:
Code: Select all
00003082753e[CPU0 ] check_cs: non-conforming code seg descriptor dpl != cpl
Code: Select all
PUSHFD
MOV EAX , DWORD PTR [EDI + 16]
AND EAX , 0x0000FFFF
PUSH EAX
MOV EAX , DWORD PTR [EDI + 28]
PUSH EAX
IRET
Any help would be really appreciated.
- Attachments
-
- Process Structure.zip
- Structure for each of the processes in my OS.
- (4.49 KiB) Downloaded 101 times
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
Okay let me tell you all what I’ve done up to this point:
1. I have coded a __CreateProcess function. It accepts the base address of a process and its property flags to be specified. It will then add the process to the Process Queue and creates SS, DS, ES, FS, GS and the CS for the process. Segment Descriptors for all of the above mentioned segment selectors have the limit of 0x0000FFFF and the DPL of 0x03 (Users).
2. The scheduler in IRQ0 can pick a process depending on its priority and also has the process’s base address. Now I want to switch to that process while having all of the required information but as soon as I, for example, change the SS in the kernel to SS of the process I get a general protection fault.
Can anybody tell me why I am getting the general protection fault and for the love of god please someone point me to an article or something that REALLY explains how task switching is done? I DO know that stacks should be switched and values should be pushed into and popped off of it but the question is HOW exactly is that done? Or am I doing something wrong with switching? Should the stack of the process have the DPL of 0x00 just like the kernel’s stack?
Oh and yes I have read Intel Manuals and as a matter of fact, they are the only reference that I trust but they have not been helpful in this matter as much as they are supposed to be. I’d really appreciate it if somebody could explain these to me.
P.S: This is the code that I have written so far for my __CreateProcess function:
1. I have coded a __CreateProcess function. It accepts the base address of a process and its property flags to be specified. It will then add the process to the Process Queue and creates SS, DS, ES, FS, GS and the CS for the process. Segment Descriptors for all of the above mentioned segment selectors have the limit of 0x0000FFFF and the DPL of 0x03 (Users).
2. The scheduler in IRQ0 can pick a process depending on its priority and also has the process’s base address. Now I want to switch to that process while having all of the required information but as soon as I, for example, change the SS in the kernel to SS of the process I get a general protection fault.
Can anybody tell me why I am getting the general protection fault and for the love of god please someone point me to an article or something that REALLY explains how task switching is done? I DO know that stacks should be switched and values should be pushed into and popped off of it but the question is HOW exactly is that done? Or am I doing something wrong with switching? Should the stack of the process have the DPL of 0x00 just like the kernel’s stack?
Oh and yes I have read Intel Manuals and as a matter of fact, they are the only reference that I trust but they have not been helpful in this matter as much as they are supposed to be. I’d really appreciate it if somebody could explain these to me.
P.S: This is the code that I have written so far for my __CreateProcess function:
Code: Select all
; ——————————————————————————————————————————————————
__CreateProcess:
; DWORD __CreateProcess (DWORD CreationFlags, DWORD ProcessASCII, void* BaseAddress); StdCall;
; Returns the Process ID of the process
; Returns 0x00000000 (Zero) upon failure
PUSH EBX
PUSH ECX
PUSH EDX
PUSH ESI
PUSH EBP
MOV EBP , ESP
; [EBP + 0x18] ; CreationFlags
; [EBP + 0x1C] ; ProcessASCII
; [EBP + 0x20] ; BaseAddress
XOR EAX , EAX
MOV EBX , OFFSET PQDTR
MOV ECX , DWORD PTR [EBX]
; See if the length of the PQDT is divisible by the length of the processes' structure -1
TEST ECX , PROCESS_STRUCTURE_LENGTH_IN_BYTES - 1
JZ .Continue1
JMP .EP
.Continue1:
; See how many processes can be put into the QUEUE
SHR ECX , 0x00000007
TEST ECX , ECX
JNZ .Continue2
JMP .EP
.Continue2:
; *EBX = PQDT
MOV EBX , DWORD PTR [EBX + 0x04]
.FindFirstFreeEntry:
MOV EDX , DWORD PTR [EBX]
TEST EDX , EDX
JZ .FoundFreeSlot
JMP .FindNextFreeEntry
; DWORD#0 = ProcessID
; DWORD#1 = CreationFlags
; DWORD#2 = Original and current time slices
; DWORD#3 = ProcessASCII
.FoundFreeSlot:
MOV DWORD PTR [EBX] , ECX
MOV EAX , DWORD PTR [EBP + 0x18] ; EAX = CreationFlags
MOV DWORD PTR [EBX + 0x04] , EAX
; Time Slice Formula = (PIT Frequency/16) * (Priority+1)
; We will only keep time slices up to 65535
MOV ECX , DWORD PTR [PITFrequency]
SHR ECX , 0x00000004
; EAX = [CreationFlags], Low 4 bits are for the priority
AND EAX , 0x0000000F
INC EAX
MUL ECX
; EAX = TimeSlice, Keep the time slice that is 1 up to 65535
AND EAX , 0x0000FFFF
SHL EAX , 0x00000010
; Current Time Slice = Number of time slices given to the current process
; For a process that is just being created this number should be zero (0x0000)s
; EAX = DWORD#2 of the process
MOV DWORD PTR [EBX + 0x08] , EAX
; Put the process ASCII in its place
MOV EAX , DWORD PTR [EBP + 0x01C] ; ProcessASCII
MOV DWORD PTR [EBX + 0x0C] , EAX
; *EBX = PQDT
; [EBP + 0x18] ; CreationFlags
; [EBP + 0x1C] ; ProcessASCII
; [EBP + 0x20] ; BaseAddress
; Create the code segment for the process
; DWORD#0 of the Code Segment Descriptor in the Global Descriptor Table
MOV EAX , DWORD PTR [EBP + 0x20] ; BaseAddress
MOV EDX , EAX
SHL EAX , 0x00000010
OR EAX , 0x0000FFFF ; Code segment limit = 0xFFFF
; EAX = DWORD#0 of the Code Segment Descriptor (CSD)
MOV ESI , EDX
SHR ESI , 0x00000010
AND EDX , 0xFF000000
OR EDX , ESI
OR EDX , 0x0040FA00
; EDX = DWORD#1 of the Data Segment Descriptor
INVOKE __AddGDTDescriptor , OFFSET GDTR , EAX , EDX
MOV WORD PTR [EBX + 16] , AX
; Create the Data Segment Descriptor for the process (DS, SS, ES, FS, GS)
MOV EAX , DWORD PTR [EBP + 0x20] ; BaseAddress
MOV EDX , EAX
SHL EAX , 0x00000010
OR EAX , 0x0000FFFF
; EAX = DWORD#0 of the Data Segment Descriptor of the new process
MOV ESI , EDX
SHR ESI , 0x00000010
AND EDX , 0xFF000000
OR EDX , 0x0040F200
INVOKE __AddGDTDescriptor , OFFSET GDTR , EAX , EDX
; AX = The segment selector of the DS, Get it in the upper WORD of the AX register
; This way we can put DS, GS, SS, ES and etc at the same time
MOV EDX , EAX
SHL EDX , 0x00000010
OR EAX , EDX
MOV WORD PTR [EBX + 18] , AX ; DS
MOV DWORD PTR [EBX + 20] , EAX ; SS, ES
MOV DWORD PTR [EBX + 24] , EAX ; GS, FS
; Set the EIP of the process to zero (The base address of the segment takes care of that)
XOR EAX , EAX
MOV DWORD PTR [EBX + 28] , EAX
; Set the general purpose registers to 0x00000000
XOR EAX , EAX
MOV DWORD PTR [EBX + 32] , EAX ; EAX
MOV DWORD PTR [EBX + 36] , EAX ; EBX
MOV DWORD PTR [EBX + 40] , EAX ; ECX
MOV DWORD PTR [EBX + 34] , EAX ; EDX
MOV DWORD PTR [EBX + 48] , EAX ; ESI
MOV DWORD PTR [EBX + 52] , EAX ; EDI
MOV DWORD PTR [EBX + 56] , EAX ; EBP
MOV DWORD PTR [EBX + 60] , 0x0000FFFF ; ESP
PUSHFD
POP EAX
MOV DWORD PTR [EBX + 64] , EAX ; EFLAGS
MOV EDX , DWORD PTR [EBP + 0x20]
MOV DWORD PTR [EBX + 68] , EDX ; BaseAddress
MOV EAX , EDX
INC DWORD PTR [ProcessesCount]
JMP .EP
.FindNextFreeEntry:
ADD EBX , PROCESS_STRUCTURE_LENGTH_IN_BYTES
DEC ECX
JNZ .FindFirstFreeEntry
.EP:
POP EBP
POP ESI
POP EDX
POP ECX
POP EBX
RET 0x0C
; ——————————————————————————————————————————————————
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.