It works with the call gates. You could have a sort of nested microkernel idea if it wasn't such a mess with each ring able to include the lower privileged ring's address spaces. And not just a free for all, but the kernel really is just the control plane, but can set up all sorts of per process descriptor tables (the LDTs).
So you'd have a tiny kernel at ring 0 which could R/W everything but wasn't responsible for much.
Under that you'd have drivers at ring 1 that can't see the kernel, but can R/W user code at rings 2 and 3.
Under that you'd have system daemons at ring 2 that can R/W regular programs but not drivers or the kernel.
And then under that you had regular processes at ring 0 that have generally the same semantics as today's processes.
Each process of any ring can export a syscall like table through the call gates, and so user code could directly invoke drivers or daemons without going throught the kernel at all. Basically IPC with about the same overhead as a C++ virtual method call.
So what happened? The exact underlying semantics didn't exactly match many OSs anyone wanted to build (particularly OSs that anyone cared about in the late 80s early 90s). And you can enforce similar semantics all in software with the exception of the cheap IPC anyway.