Since the operating system provides an hardware abstraction layer, and since it must provide resource sharing, every access to the hardware from user programs should be done through the operating system. To enforce this in a secure way, and prevent malicious or buggy applications to mess up with the hardware, a protection is needed at the hardware level.
Hardware must provide at least two execution levels:
So, we define two spaces at software level:
This is the traditional design of Unix systems. Every part which is to be accessed by most programs which cannot be put in a library is in the kernel space:
Many system calls are provided to applications (more than 250 for Linux 2.4), to allow them to access all those services.
This design has several flaws and limitations:
Only parts which really require to be in a privileged mode are in kernel space :
Many critical parts are now running in user space :
A single user-space program (server) handles everything that belonged to the kernel. The two most common examples are MachOS and L4Linux (a port of Linux as a user-space server on top of the L4 microkernel). This split allows better hardware independence of the operating system itself, a slightly easier development, and a limited improvement in overall security (since the code running in the user-space, the server cannot directly access the hardware).
But this design still has most drawbacks of monolithic systems: if the monolithic server crashes, the whole system crashes; it's impossible to add code to it without being root, changing most of the code requires a reboot, ...
All features are now split into a set of communicating processes, with each of them handling only a very specific task (like a TCP/IP server or an ext2fs server). This modularity allows to replace components easily, an easier developement, a far better fault-tolerance (since a crash of one of the servers cannot corrupt the internal state of any other), and far more flexibility for the end user.
But, like always in computer science, all those benefits come with several drawbacks: the communication between all the servers can slow down the whole system, and the definition of a strict set of interfaces and protocols for communication between those servers is an extra work to do.