I am planning to write my own OS for Intel 64bit architecture, and I need to implement General Protection mechanism. I will not be using virtual memory or paging, but physical addressing. I do not know yet how exactly to implement it, but I have a general idea and it goes something like this:
- On startup, the kernel will be loaded and lets suppose it will launch the first userland process called ‘filemanager’ which will save some data to disk on some signal received from another process.
- The filemanager will have a code segment, a stack segment and a data segment.
- During the normal operation the kernel will launch another processes, which will take the memory from the available physical memory heap, managing which the kernel takes care of.
- Lets suppose, the ‘filemanager’ process needs to get more ram because its data segment became full. It sends a syscall to the kernel to ask for another data segment. The kernel gives it the requested memory chunk which is located physically in different place and it is not contigous in relation to the first segment.
Now here is the question, how does the application (‘filemanager’) will handle 2 segments ? Because the General Protection mechanism implemented by Intel, as I understand it, can only check for the limits of the current segment which is stored in ‘DS’. But in case of filemanager, we have 2 segments now. Do I have to switch manually from one segment to another inside the application? It would be a pain to code an application like this
because you would have to write something like this:
__asm('set DS register to the address and length of segment 1');
for (i=0;i<size_of_segment1;i++) {
segment1_data[i]= some_processing();
}
__asm('set DS register to the address and length of segment 2');
for (i=0;i<size_of_segment2;i++) {
segment2_data[i]= some_processing();
}
This is only for 2 segments, but imagine the application will have to ask the kernel for 1000 or more data segments, how do you implement General Protection mechanism so it can be fast and easy to program for the developer? One way to do this would be to modify the GCC and it would have to insert the assembly code to load the corresponding data segment into DS register before accesing each data segment, but that looks like a lot of work. Are there some other ways?
Continuing…
5. Shared memory. Lets say another process ‘editor’ needs to save some data through the ‘filemanager’ process. A shared memory mechanism must be provided by the OS. The ‘editor’ process will request the kernel to add the data segment of the ‘filemanager’ process. The ‘filemanager’ will confirm the request and the new segment will be added to editor’s data segment list so it could legally write to it. The question again, is the same, how do I switch between multiple segments inside ‘editor’ the way it is transparent for the application and secure for the overall system?
Note: I will not use paging in my OS, all the memory addressing will be physical, and it will be in 64 mode.
If you are in 64-bit mode than there is no segment register at all: the contents of CS, DS… are simply ignored. Addresses are 64-bit values in a “flat” address space. Segment registers are used in 16-bit and 32-bit modes, although most 32-bit OS set all segments to begin at address 0 and span over 4 GB, so that they may forget them forever. If you use segment registers (such that application code may use several “segments”) then, at the C level, the C compiler must know about them, and a “pointer” will include both segment and offset value. Setting a segment register is expensive, so if you can do without segments, performance is better. Which explains why 32-bit OS like Linux or Windows do not use segments, and, correspondingly, CPU vendors have adapted and simply omitted segment support in 64-bit mode.
The real question is then: do you want memory protection between applications ? Memory protection is about blocking a process from reading or writing memory that it should not read/write, namely memory used by another process or the kernel. You can do without memory protected memory if application programmers never write code with bugs (mmh… how plausible is that ?) or if applications are written in a language which includes systematic checks which prohibit invalid memory accesses (e.g. Java)(the JNode project was about an OS in Java, but I do not know if it is still alive). To get memory protection, you use the MMU. The MMU works by “pages”: the address space is split into fixed-size pages (4 kB each on x86) and the OS installs tables in RAM which tell the MMU where each page physically is. Paging, disk-based virtual memory… use the MMU. But you can also use the MMU “just” for memory protection (you set it up so that each page is either present at a virtual address which is equal to its physical address, or marked “absent” if access to that page is not currently allowed).
Not using the MMU at all can give a performance boost in some very specific situations (I know someone who was doing that for sieving, as part of a big cryptographic-oriented computational effort: sieving was almost 100% random-looking memory accesses in a 1 GB array; the MMU was implying three cache misses instead of one per access, so going without the MMU was making the whole thing three times faster — but that’s really an edge case). On the other hand, the MMU makes life easier for application developers, especially since it allows every application to have its own normalized address space (e.g. you can compile and link C code in advance).