Hey guys!
I am currently proceeding my development on my operating system.
By now paging works as follows: The kernel has its own directory. I can create user space directories by giving a start and end address (Paging::createUserspace(0x10000000, 0x20000000) for example). To do that I copy the kernel directory to a blank directory, create new pages for user space and insert them into the new directory. Is this approach correct?
So now the time has come for me to implement multitasking, but I have a little problem understanding it right. This is how I got it:
- Paging is enabled & user directory exists
- Current directory is kernel directory
- The scheduler calls
- Store the kernel register values
- Switch to the user directory
- Restore the processes register values
- Let the process work until scheduler calls
- Store the process register values
- Switch back to kernel directory
- Restore the kernel register values
- Repeat
Is this right? The only problem I have there is, if I copy the kernel directory to the user space directory to keep executing, isnt there the danger that the user code could change the contents of my kernel memory? Do I really have to copy the kernel directory or is there another way?
Thanks in advance!
Multitasking comprehension problem
Re: Multitasking comprehension problem
Hi,
In this case task switching typically goes like this:
Of course this isn't the only way (there are alternatives that do work) - it is the most common way it's done, and also probably the easiest to understand.
Cheers,
Brendan
Normally the kernel is mapped into every address space. For example, each process might only be able to use addresses from 0x00000000 to 0xBFFFFFFFF in its virtual address space, and the kernel might be mapped into the addresses from 0xC0000000 to 0xFFFFFFFF so that it is the same in every address space.max wrote:Is this right? The only problem I have there is, if I copy the kernel directory to the user space directory to keep executing, isnt there the danger that the user code could change the contents of my kernel memory? Do I really have to copy the kernel directory or is there another way?
In this case task switching typically goes like this:
- a task is running happily
- something happens to cause the CPU to switch to the kernel. This might be because the task called the kernel's API, or because the task caused an exception, or it might not have been caused by the task itself (e.g. any IRQ).
- the kernel starts handling whatever caused it to get the CPU (the kernel API call, the exception, the IRQ)
- the kernel decides (for whatever reason) to do a task switch. Reasons include the task using all the time it was given, the task blocking (waiting for something), an IRQ causing another task to unblock/stop waiting and preempt, etc.
- the kernel saves its registers for this task
- the kernel loads its registers for the next task (possibly including CR3)
- the kernel finishes whatever it was doing, and returns control back to the task
- a task is running happily again
Of course this isn't the only way (there are alternatives that do work) - it is the most common way it's done, and also probably the easiest to understand.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: Multitasking comprehension problem
Let's start with the proper terminology: the "address space" is the memory laid out to be seen by a process (or chip). A "directory" is an index, typically on your harddisk.
On x86, paging can make the linear address space look different from the physical address space. The mapping is made by data structures called "page tables" and "page directories". Creating a new page directory and page tables implies creating a new address space.
Address spaces contain the entirety of usable memory. Protection mechanisms exist to cut that into separate parts where access is denied for userspace, for writing, or for using directly as code.
Also, by duplicating the kernel into other address spaces, the kernel technically no longer has an address space of its own. Instead you get the almost universal standard of the kernel being present in all address spaces. Many implementations don't treat that first address space different in any way. It only becomes special because its age makes it quite likely to hold a mission-critical task.
On x86, paging can make the linear address space look different from the physical address space. The mapping is made by data structures called "page tables" and "page directories". Creating a new page directory and page tables implies creating a new address space.
Address spaces contain the entirety of usable memory. Protection mechanisms exist to cut that into separate parts where access is denied for userspace, for writing, or for using directly as code.
It could probably work, but it looks rather arbitrarily restricted. Typically a portion of the address space is reserved for the kernel, and the remainder is userspace by design. In between there are large gaps of space that do not point to anything. Hard limiting a process to 256MB RAM max is not a good idea either to keep a more elaborate firefox session alive for long. Also, I hope you are not preallocating that amount of RAM either.max wrote:I am currently proceeding my development on my operating system.
By now paging works as follows: The kernel has its own directory. I can create user space directories by giving a start and end address (Paging::createUserspace(0x10000000, 0x20000000) for example). To do that I copy the kernel directory to a blank directory, create new pages for user space and insert them into the new directory. Is this approach correct?
Also, by duplicating the kernel into other address spaces, the kernel technically no longer has an address space of its own. Instead you get the almost universal standard of the kernel being present in all address spaces. Many implementations don't treat that first address space different in any way. It only becomes special because its age makes it quite likely to hold a mission-critical task.
Re: Multitasking comprehension problem
Okay, so while I logged in we already got two answers on how it's typically done, so I can leave that part out and just have a look at what you were planning to do.
First thing is that you don't really have a separate kernel page directory. You already copy it into each userspace directory because you need it for IDT, GDT, TSS and some of the kernel (the task switching code) is running with the userspace directory, too. Now if the kernel is already mapped, there's little reason to switch to a different directory, because the current one is already good enough.
The other thing that you don't really need depending on what your kernel looks like is saving and restoring kernel register state. If you switch tasks somewhere in the middle of C code, then you probably need it. More commonly, however, is C code only called from an interrupt handler that effective implements the task switch. Then you don't need the register values any more.
I remember that you speak German, so these parts of the Lowlevel tutorial series could help you a bit, they explain that approach in more detail:
http://www.lowlevel.eu/wiki/Teil_5_-_Interrupts
http://www.lowlevel.eu/wiki/Teil_6_-_Multitasking
I can't call it exactly wrong, because I think you could get something working this way, but it doesn't make a whole lot of sense.max wrote:I am currently proceeding my development on my operating system.
By now paging works as follows: The kernel has its own directory. I can create user space directories by giving a start and end address (Paging::createUserspace(0x10000000, 0x20000000) for example). To do that I copy the kernel directory to a blank directory, create new pages for user space and insert them into the new directory. Is this approach correct?
First thing is that you don't really have a separate kernel page directory. You already copy it into each userspace directory because you need it for IDT, GDT, TSS and some of the kernel (the task switching code) is running with the userspace directory, too. Now if the kernel is already mapped, there's little reason to switch to a different directory, because the current one is already good enough.
The other thing that you don't really need depending on what your kernel looks like is saving and restoring kernel register state. If you switch tasks somewhere in the middle of C code, then you probably need it. More commonly, however, is C code only called from an interrupt handler that effective implements the task switch. Then you don't need the register values any more.
I remember that you speak German, so these parts of the Lowlevel tutorial series could help you a bit, they explain that approach in more detail:
http://www.lowlevel.eu/wiki/Teil_5_-_Interrupts
http://www.lowlevel.eu/wiki/Teil_6_-_Multitasking
- max
- Member
- Posts: 616
- Joined: Mon Mar 05, 2012 11:23 am
- Libera.chat IRC: maxdev
- Location: Germany
- Contact:
Re: Multitasking comprehension probleme
Hey guys,
thank you for the information!
Ive read a bit more and finally understood my problem.
Ill keep you on track with my further progress!
@Kevin ja stimmt ich komm aus Deutschland
Greetings,
Max
thank you for the information!
Ive read a bit more and finally understood my problem.
Ill keep you on track with my further progress!
@Kevin ja stimmt ich komm aus Deutschland
Greetings,
Max
Re: Multitasking comprehension problem
The above answers are definitely the standard and good answer, but I would like to add a few ideas on top of that.
In some stage your kernel will be more or less allocate address dynamically and such address are required(*) to be visible across processes,
to keep such address zone synchronized, there are a few approach:
A 32-bit 2 level paging: version control
- Keep a master copy + a version number; whenever the zone changes (note that an allocation as small as 4KB might require to propagate to all process), the changes are commited to the currently page structures and the master copy, and the version number is incremented.
Upon switching address space, if the target version is not equal the master version, a synchronize is performed.
PAE or PM4L:
- Since the kernel may occupy a level2 node, which cover gigabyte scale which is more or less enough, if you limit the size of global address zone you may just share the same (one, or a few) node on all process's address space. Changes on level1(PTE) will be synchronized by design.
- or extend to use the version control method for unlimited size.
*) If a kernel allocate some memory, which require adding new PT/PD entry; then later on it switch to another process, in the new process the page structure may be out of sync and that newly allocated memory is no longer accessible - that's why a synchronization mechanism is required, either done when address layout changes, or done lazily until it is needed.
In some stage your kernel will be more or less allocate address dynamically and such address are required(*) to be visible across processes,
to keep such address zone synchronized, there are a few approach:
A 32-bit 2 level paging: version control
- Keep a master copy + a version number; whenever the zone changes (note that an allocation as small as 4KB might require to propagate to all process), the changes are commited to the currently page structures and the master copy, and the version number is incremented.
Upon switching address space, if the target version is not equal the master version, a synchronize is performed.
PAE or PM4L:
- Since the kernel may occupy a level2 node, which cover gigabyte scale which is more or less enough, if you limit the size of global address zone you may just share the same (one, or a few) node on all process's address space. Changes on level1(PTE) will be synchronized by design.
- or extend to use the version control method for unlimited size.
*) If a kernel allocate some memory, which require adding new PT/PD entry; then later on it switch to another process, in the new process the page structure may be out of sync and that newly allocated memory is no longer accessible - that's why a synchronization mechanism is required, either done when address layout changes, or done lazily until it is needed.
Re: Multitasking comprehension problem
It's probably worth noting that you don't need a separate master version, but you can just keep a pointer to the most recently updated context, which then serves as the master copy.bluemoon wrote:- Keep a master copy + a version number; whenever the zone changes (note that an allocation as small as 4KB might require to propagate to all process), the changes are commited to the currently page structures and the master copy, and the version number is incremented.
Upon switching address space, if the target version is not equal the master version, a synchronize is performed.
Other people just decide that a few kilobytes aren't worth the hassle and just allocate all page tables for the kernel memory area upfront, so that the page directory for this area never changes.