Hi Tim,
Tim Wescott wrote:
> I pontificated by saying that a complete RTOS should have both binary
> and counting semaphores, and someone called me on it by asking just what
> a "complete" RTOS is.
Depends on how complete you define "complete" to be! :>
An OS is intended to manage the hardware to provide a
scaffolding on which the developer can drape his application.
MS's idea of an OS includes a GUI, file system, drivers, spyware,
etc.
I see the basic services that an *embedded* OS should support
to include:
- memory management
- CPU management
- time management
- communications
The exact details and "richness" of the various services varies
with the complexity of the programming model, targeted applications
and hardware platform.
To skip ahead to your final comment: "No, an OS doesn't need
to include *drivers*. Just like it doesn't need to include
*applications*!" (though some do -- on either or both counts)
Memory management can vary in complexity from *nothing*
(user handles all of his own memory management staticly)
through simple (static buffer pools) and complex (dynamic
memory management). Single shared address spaces vs.
multiple (often *protected*) address spaces. Memory
region attributes vs. no "coloring".
CPU management allows the application(s) to share the CPU
in some "equitable" fashion (the application defines
"equitable"). How isolated individual tasks/processes/threads
are determines the depth of services the developer will
likely need in that environment. E.g., can anyone start
a task? Can anyone *kill* a task? Can one task manipulate
another task's *state*? What sorts of synchronization
primitives do you provide? etc.
Time management is one of the few items whose role *in* an
OS I vassalate about. Ideally, time can be handled outside
the OS -- just like drivers can be "outside" the OS. But,
it is *so* scrumptious that it really wants to be a core
service -- so that other services can exploit it (more later).
This includes being able to measure elapsed time, create
reasonably accurate delays and, optionally, maintain a
time-of-day clock (though this can be easily externalized).
Communications can be an issue depending on the programming
model chosen for the OS. I.e., if each process/task/etc.
has a separate *protected* address space, then how do processes
exchange information? I.e., the OS needs to play a mediating
role. In flat, unprotected address space models, this might
be as simple as relying on a mutex or a monitor governing
a shared memory region/object.
How do you support IPC? *Do* you support RPC? What is the
mailbox/port model used? Can you handle out-of-band data?
Is data *typed* or *untyped*? etc.
For real-time systems, communications can quickly be *the*
bottleneck -- especially if a system is poorly designed or
partitioned (leading to unnecessary communications).
> I realized that my definition of a "complete" RTOS is pretty fuzzy --
> mostly a mismash of every feature that I've ever wanted to use in an
> RTOS, with none of the features that I didn't want to use. This is
> ironic, because I also made a snide comment about RTOS vendors being
> self-centered.
I think many so-called RTOS's are little more than MTOS's.
Some might be fast/slick MTOS's but MTOS's nonetheless.
(writing an MTOS is a weekend task) RTOS's have higher
standards to meet (no, not "just fast"... an RTOS can
be *slow* as long as it is *deterministic*)
> I know what the minimum set of features I want in an RTOS -- basically a
> prioritizing scheduler with deterministic performance, that allows tasks
> to be started under the programmer's control from software entities
> outside of the task. This gives you all the tools you need to fire off
> tasks from ISRs or other tasks, to block on resources, pass messages, etc.
Often, implementations are crippled in silly ways -- "You can only
use *these* routines/services from ISR context", etc. These are
bugs waiting to happen. :<
> But what is a "complete" RTOS then? And if there is one, does anyone
> want it? Is a "complete" RTOS just like a CISC instruction set, wasting
> code space on features that one may never use, just so a vendor can crow
> about it being "all there"?
You can, theoretically, build the OS as a library of routines/services
and only link in the ones that are needed (since most embedded
systems are "completely defined" at deployment). But, if the
implementor heavily integrated things, then you tend to get
stuck with a lot that you may or may not need (or, you have
different versions of the OS that you deploy in different
circumstances)
> And do you think my "minimum necessary" RTOS really includes everything
> you need? Or do you think that it's just not functional until you can
> pass messages and have Bertie Bott's Every Flavor Flag and Ethernet and
> USB and a hard drive and cotton candy on a stick?
I rarely use the *exact* same OS twice. There is usually room
to tweek things to better "fit" the application. Remember,
we tend (C.A.*E*) to design things that have specific, well
defined responsibilities. So, you *know* what services you
need from the OS and don't have to include things "in case
they *might* be needed".
I have found, however, that I *really* like working in a
rich environment. It presents lots of different approaches
to particular design issues that are just too "expensive"
to implement, otherwise. And, *consistency* is a HUGE win!
I.e., being able to access all services in a similar fashion.
And, pushing off *into* the service things that would just be
tedious and "cluttering" to do otherwise (e.g., letting services
"wait" for resources that *I* need instead of me having to
explicitly spin-wait for those)
E.g., in a protected memory space environment, I can "map"
a file into my address space instead of having to literally
*copy* it into RAM. Of course, if I *want* to use RAM to
copy the file (or parts thereof), I can also do that. But,
I am free to trade time for space AT RUN TIME instead of
being *compelled* to go one way or the other.
Likewise, being able to "share" code segments safely (so
task A doesn't munge some aspect of a shared library
that corrupts task B's execution therefrom).
I think once you get exposed to a variety of "facilities",
you are better able to figure out which you *really* need
in any particular OS when faced with a set of application
requirements.
Lately, I am experimenting with naming schemes for objects
so that tasks can deal with them at more abstract levels
(vs. hard-coding things).