* No paging. Entire program in memory at all times. Although it was possible to put a paging library inside an application and let it manage its own paging, which was done for gcc.
* A real time CPU dispatcher. Real time priorities were strictly preemptive. Unblock a higher priority task, it starts now, not when the dispatcher gets around to it.
* The usual test for a hard real time OS is that you have an interrupt routine that senses an external pin. It unblock a user level task when the pin goes high. The user level task turns on an output pin. You hook up a scope and a square wave generator, and watch the latency. If there are outlier values on the scope, something is broken. One implication is that stuff below the OS, such as anything running in system management mode after boot, has to be eliminated. It cannot be allowed to steal cycles.
* While real time work is going on, you can still run compiles and web browsers. They get preempted. I was impressed that that worked. Our real time application had a hardware stall timer, and if the commands coming out were late, a relay tripped and everything shut down.
So, it's absolutely possible to do this. Downsides:
* You're way out of the mainstream.
* You're going through the general case every time. Fast paths are unwanted.
* Everything is always on. Power consumption is constant. No CPU slowdowns, no sleep modes, no battery saving. This is just fine when you're running a rolling mill, and 99+% of the power is driving the heavy equipment. Not so good when you're running a credit card terminal. Battery powered devices cannot run well in this mode.