What you've done is very impressive. But, as I've said elsewhere, it's overshadowed by you insisting on calling it a "kernel" when it's not. Please stop doing that, and please don't include that claim in your future write up of how it works. The thing is, some submissions to Hacker News are by people who really have written their own actual kernel so readers have no reason to doubt your claim. Many will believe you really have written (or ported) an actual operating system kernel and then be disappointed when they realise you haven't, rather than excited by what you have achieved.
> My kernel is implemented in userspace
Your kernel is not implemented in userspace, because you have not written a kernel. User mode Linux is an actual real kernel running in user space. It is just a totally different concept.
> I should use the term "kernel emulation"
No, you shouldn't. This is coming a bit closer to the truth but still wrong. You have emulated **the system call interface** of a kernel. The implementation of the system-call interface is only a teeny tiny part of a kernel.
> ... the code of my kernel emulation (libposix-6.1.0.dll) …
You mean, the code of your system-call interface emulation.
> Making my DLLs load at an address just below the 0x10000000 boundary makes MinC programs load faster, in effect creating a space for my kernel, e.g. "kernel space".
No, that is not "kernel space". None of those DLLs (netapi32.dll, ws2_32.dll, iphlpapi.dll and shell32.dll) are part of the kernel. They all run in user mode. Please read the Wikipedia article on user mode vs kernel mode that I linked to above, and understand that none of your code is running in kernel mode at all.
> By the way, Microsoft also has a user space implementation of their kernel, called kernel32.dll, implementing Win32 APIs in user space.
kernel32.dll does run in user space but it is not a kernel or an implementation of a kernel. Its name is a historical artefact. (Its name can be traced back to a component that, 40 years ago, in Windows 1.0, actually was a kernel. But even by Windows 3.0 it was already just an interface to some kernel services, not a kernel implementation.)
> ... after calling into the various Windows DLLs which are not part of the Windows NT kernel. For instance, the WriteFile() function ...
None of these DLLs are part of the kernel! (By the way, "WriteFile()" function is implemented in kernel32.dll. But it's still not part of the NT kernel.)
> Turns out I needed what is called a "context switch", saving the contents of all registers onto the stack, calling my kernel emulation and then restoring the registers from the stack. In Linux, this mechanism is triggered by a special interrupt (int 0x80).
No!
* "Context switch" means switching between user-mode processes, not between user mode and kernel mode. It is done by the scheduler within the kernel.
* int 0x80 triggers a system call on Linux, not a context switch.
* A system call doesn't trigger the save/load of registers, as you said. Instead, you need to save/restore the registers yourself. (i.e., to make a system call on Linux you would do: (1) stash registers on stack; (2) call int 0x80; (3) restore registers.)
* Saving registers and restoring them is part of many function calls, not just system call interfaces. It's just that usually the compiler takes care of it for you, rather than something you need to hand craft in assembler.
--------------
Here's how I'd attempt to describe what you've done in a way that isn't misleading:
Typically, if you're going to going to emulate the user-mode API of one operating system within another, you'd simply implement the user-mode functions of one directly in terms of the other. For example, you might implement the Posix open() API by reformatting the arguments and directly calling the Win32 OpenFile() function. In MinC, the user-mode API is instead emulated at the system call level. This means that the user mode DLL (libposix-6.1.0.dll) has the same code as the user-mode API in BSD except that the system-call interrupt is replaced with a call to the syscall emulation code.
Thanks for taking the extra time making me consider dropping the term "kernel". But I need a term wich reflects my efforts. So I fed your description at the end into ChatGPT, asking for a one-word description. This is what it came up with:
Paravirtualized – commonly used when an interface mimics low-level system behavior but redirects to a host implementation; this fits your syscall-level emulation.
Interposed – describes the interception and redirection of system calls or API behavior.
Emulated – broad but accurate; emphasizes imitation of behavior at the syscall layer.
Rehosted – suggests the POSIX environment has been adapted to a different host OS.
Substituted – captures the idea of replacing one system mechanism with another.
Intercepted – emphasizes the capture and redirection of syscalls.
Translated – suitable if there's transformation between POSIX calls and Windows kernel APIs.
Adapted – highlights compatibility through minimal changes.
Bridged – suggests a connection or interface between incompatible systems.
Virtualized – somewhat general, but appropriate if the environment feels like a lightweight VM layer.
Interesting, but not very creative. Something like Ted Nelson's "transclusion". Suggestions? Anyone?
What you've done is very impressive. But, as I've said elsewhere, it's overshadowed by you insisting on calling it a "kernel" when it's not. Please stop doing that, and please don't include that claim in your future write up of how it works. The thing is, some submissions to Hacker News are by people who really have written their own actual kernel so readers have no reason to doubt your claim. Many will believe you really have written (or ported) an actual operating system kernel and then be disappointed when they realise you haven't, rather than excited by what you have achieved.
> My kernel is implemented in userspace
Your kernel is not implemented in userspace, because you have not written a kernel. User mode Linux is an actual real kernel running in user space. It is just a totally different concept.
> I should use the term "kernel emulation"
No, you shouldn't. This is coming a bit closer to the truth but still wrong. You have emulated **the system call interface** of a kernel. The implementation of the system-call interface is only a teeny tiny part of a kernel.
> ... the code of my kernel emulation (libposix-6.1.0.dll) …
You mean, the code of your system-call interface emulation.
> Making my DLLs load at an address just below the 0x10000000 boundary makes MinC programs load faster, in effect creating a space for my kernel, e.g. "kernel space".
No, that is not "kernel space". None of those DLLs (netapi32.dll, ws2_32.dll, iphlpapi.dll and shell32.dll) are part of the kernel. They all run in user mode. Please read the Wikipedia article on user mode vs kernel mode that I linked to above, and understand that none of your code is running in kernel mode at all.
> By the way, Microsoft also has a user space implementation of their kernel, called kernel32.dll, implementing Win32 APIs in user space.
kernel32.dll does run in user space but it is not a kernel or an implementation of a kernel. Its name is a historical artefact. (Its name can be traced back to a component that, 40 years ago, in Windows 1.0, actually was a kernel. But even by Windows 3.0 it was already just an interface to some kernel services, not a kernel implementation.)
> ... after calling into the various Windows DLLs which are not part of the Windows NT kernel. For instance, the WriteFile() function ...
None of these DLLs are part of the kernel! (By the way, "WriteFile()" function is implemented in kernel32.dll. But it's still not part of the NT kernel.)
> Turns out I needed what is called a "context switch", saving the contents of all registers onto the stack, calling my kernel emulation and then restoring the registers from the stack. In Linux, this mechanism is triggered by a special interrupt (int 0x80).
No!
* "Context switch" means switching between user-mode processes, not between user mode and kernel mode. It is done by the scheduler within the kernel.
* int 0x80 triggers a system call on Linux, not a context switch.
* A system call doesn't trigger the save/load of registers, as you said. Instead, you need to save/restore the registers yourself. (i.e., to make a system call on Linux you would do: (1) stash registers on stack; (2) call int 0x80; (3) restore registers.)
* Saving registers and restoring them is part of many function calls, not just system call interfaces. It's just that usually the compiler takes care of it for you, rather than something you need to hand craft in assembler.
--------------
Here's how I'd attempt to describe what you've done in a way that isn't misleading:
Typically, if you're going to going to emulate the user-mode API of one operating system within another, you'd simply implement the user-mode functions of one directly in terms of the other. For example, you might implement the Posix open() API by reformatting the arguments and directly calling the Win32 OpenFile() function. In MinC, the user-mode API is instead emulated at the system call level. This means that the user mode DLL (libposix-6.1.0.dll) has the same code as the user-mode API in BSD except that the system-call interrupt is replaced with a call to the syscall emulation code.