That __is__ true, Doom can run on bare metal, but that's a fairly hacky solution in many ways. Doing it properly in userspace with a LibC and conceptually POSIX syscalls require a bit more effort. It requires a list of LibC functions and POSIX utilities as well as a few OS specific implementations.
Umm that's probably the extreme end of OSDev which I likely wouldn't be able to do, at least not for a driver you can buy. Qemu's emulated GPU is documented decently and could be possible, but things like nvidia GPUs are badly documented (and until recently, the docs were fully closed source) - even Linux has issues with this (and I actually see a few other hobby OS devs who just use Linux's GPU drivers in the end). There aren't a lot of things I've marked as near impossible but writing a genuinely decent GPU driver for a common GPU isn't really something I imagine I'll ever be able to do sadly.
The docs for intel’s integrated offerings seem pretty good, I’m planning to do a driver for those on my toy OS. Anything else is hit and miss in terms of availability of information unfortunately.
Well, it can do a little more than run Doom, that's just the latest milestone as to what was ported last. It wasn't a huge effort, maybe took a week or so to get Doom itself running including adding libc requirements for Doom, but what came before that was a fair bit more groundwork. I used DoomGeneric, which is basically a fork of Doom which is made to be very portable. I hope I answered your question, I might have misunderstood.
I use paging for virtual memory, which gives each process it's own address space. I have a round-robin scheduler connected to the PIT driver, so every 10ms the PIT fires an interrupt which triggers the scheduler, which selects the next task, saves the current state of the previous task, switches to the new address space, switches the stack, restores registers of the task, then uses the iretq instruction to switch to ring 3 user mode and jump to the instruction pointer.
Thanks for that explanation. I've been doing some low-level programming lately, and I'm getting interested on running stuff bare-metal. Every previous description of multitasking I've seen has been very hand-wavy.
Yeah no problem. Multitasking isn't really complex - it's generally split into two categories: collaborative and preemptive. Collaborative multitasking is simply having user programs call a yield syscall which tells the kernel that it's ready to give up control of the CPU and it switches to the next task, but this is not very secure and is uncommon on newer systems. Alternately, preemptive scheduling generally has a timer which goes off regularly which automatically switches to the next task. Choosing the next task is the next part which the simplest one is round robin, where you literally just have a list of tasks and it always selects the next task and gives each task an equal amount of time. You also have SMP versions (that work for multiple cores) and also priority-based schedulers (which give certain tasks, such as system daemons, more processing time). Hopefully that extra info helped a bit!
Not so much about security but stability. If a program enters an infinite loop then it never yields and the entire system is hung. Hopefully you have an interrupt you can fire off (like ctl-alt-del) that can wake up the kernel and allow you to take action.
Haha not quite at the point Hurd has gotten. I'd be very happy if I could last that long on one project though, I have a tendency to get bored after a few months sadly.