In order to compare the different real-time operating systems we first need to define a few terms.
There is no exact definition of RTOS as real-time is dependent on the application. In robotics for example real-time could mean a reaction time of a few tens of milliseconds, while on a solid state disk (SSD) the controller is expected to react in a few microseconds. Real-time is commonly distinguished between hard and soft. In hard real-time a result must be delivered before a deadline, or it will have catastrophic effect on the system [2, p. 8]. In soft real-time a timely result is expected, a late result however is tolerable. Although a task on a soft RTOS might run late, it is still expected to run in deterministic manner in contrast to a general purpose OS like GNU/Linux or Windows.
An operating system consists of a kernel and modules for hardware access, file access, networking and others. Whether some of these modules run inside or outside the kernel is dependent on the kernel architecture. Most general purpose operating system use a monolithic kernel (GNU/Linux, Windows, macOS), with many modules running inside the kernel. An OS where drivers, file systems and other services are pushed to the user mode is called microkernel. [8, pp. 65]
The main purpose of a scheduler is to share the CPU fairly between multiple tasks that are ready to run [8, pp. 149]. Scheduling adds overhead because the CPU context has to be switched from one task to another. Still, a scheduler can optimize CPU load when one task is waiting for a resource by switching to a ready task.
Figure: Cooperative (non-preemptive) scheduling
Tasks are called cooperative or non-preemptive if they cannot interrupt (preempt) each other. There can only be one priority for all tasks because a task has to give up the CPU by itself. When a task has finished it yields and calls the scheduler as illustrated in the figure. This type of scheduling has minimal overhead as the tasks can run from a single stack and context switches only when absolutely necessary. Nevertheless, the programmer has to ensure a task yields otherwise blocking all the other tasks.
Figure: Priority based preemptive scheduling
With the preemptive scheduling method tasks can be interrupted before they have finished. If priorities are supported, the task with the highest priority will run and preempt the others. the figure shows an example where an interrupt
Preemption also applies for tasks of the same priority when a scheduler implements time slicing or round-robin. In that case the scheduler is periodically called after a defined amount of time. The scheduler will not wait for a task to finish but rather just switch from one task to another with the same priority.
Priority based preemption does also occur on bare-metal systems when a hardware interrupt is triggered. At that point the current program is preempted and the interrupt service routine is called.
Processes on PCs are isolated in their virtual address space. Threads on the other hand run within a process and share an address space. Processes cannot directly communicate. The operating system is responsible to pass on messages and to provide synchronization methods for shared resources. This is called inter-process communication (IPC). [8, pp. 119]
Microcontrollers do not have memory management unit (MMU), which is needed for virtual address spaces, resulting in one continuous address space. Thus, only threads/tasks instead of processes are available. But passing information and synchronization is also needed between tasks in the way it used between processes.
As soon as multiple task run in parallel or pseudoparallel access to commonly used resources must be synchronized to avoid race conditions. These accesses can be protected using mutual exclusion (mutex) or semaphores, depending on the resource. Mutex and semaphores are provided by the operating system via system calls.