When I found myself staring at the first draft of this book I started thinking that it was going to be a difficult read because I'd spent so many years intimately involved with the design and development of QNX Neutrino. But I was wrong! I found this book easy to read and very enjoyable because of the way Rob combines the QNX philosophy (“Why things are the way they are”) with good habits applicable to any realtime programming project. This book is suitable for people who've never seen Neutrino before, or those who've used it extensively.
For people who've never used Neutrino, the book gives an excellent tutorial on how to use it. Since Rob himself comes from a QNX 2 and QNX 4 background, his book is also great for people who've used a QNX operating system before, because they share a common ground.
As for myself, I was first introduced to QNX at an insurance company in the mid-1980s. This insurance company primarily used an IBM mainframe, but they wanted to shorten the time required to calculate quotes on corporate insurance. To do this they used networks of 8 MHz 80286 ATs running QNX 2. They distributed their data using QNX native networking, allowing access to all customer data files from any QNX machine. This system was well-designed using the QNX client/server philosophy and I was hooked on QNX.
When I joined QSS at the start of 1991, QNX 4 had just been released. QNX 4 was developed to conform to the just-approved POSIX 1003.1 specification which would make it easier to port public domain UNIX code than it was with QNX 2, and it would conform to a genuine standard. In a few years we started thinking about the next-generation operating system. The current group of less than 15 developers started meeting to discuss anything we'd like to do differently and things that we'd need in the future. We wanted to support the newer POSIX specifications and make it easier to write drivers. We also didn't want to lock ourselves to the x86 processor or “fix” anything that wasn't broken. The ideas that Dan Dodge and Gordon Bell started out with when they created QNX are still in Neutrino today — ideas like message-passing, having a small, lean kernel, providing fast, realtime response, etc. Complicating the design was the goal of Neutrino being even more modular than QNX 4 (for example, we wanted to provide a fully-functional kernel that you could link against, allowing for more deeply embedded systems than QNX 4). In 1994 Dan Dodge and I started working on the updated kernel and process manager.
As those of you who've been using QNX products for a long time already know, writing device drivers for QNX 2 was a hair-raising experience. You had to be very careful! In fact, most developers started with the QSS-supplied source for the spool device and carefully tweaked it to do whatever they wanted. Only a few people tried writing disk drivers, as this required specialized assembly language stubs. Because of this, almost nobody ended up writing drivers for QNX 2. In QNX 4, writing drivers was made much easier by making all I/O operations go through a standard, well-defined, message-passing interface. When you did an open(), the server received an open message. When you did a read(), the server received a read message. QNX 4 capitalized on the message passing theme by using it to decouple clients from servers. I remember when I first saw the beta version 3.99 (a QNX 4 pre-release version) and thinking, “Wow! This is elegant!” In fact, I was so enamored with this, that I immediately wrote a QNX 2 read-only filesystem using the new message-passing interface; it was easy now!
For Neutrino, the process manager was being designed with three main separate functions: pathname space management, process creation (attributes, destruction, etc.), and memory space management. It also included several sub-services (/dev/null, /dev/zero, image filesystem, etc.). Each of these acted independently, but all shared the common code for processing the messages. This common code was very useful, so we decided to take all the common code and make a cover library for it. The “Resource Manager” library (or, as Rob likes to pronounce it, to my utter dismay, rez-mugger :-)) was born.
We also found that most resource managers wanted to provide POSIX semantics for their devices or filesystems, so we wrote another layer on top of the resource manager layer called the iofunc*() functions. This lets anybody write a resource manager, and have it automatically inherit POSIX functionality, without any additional work. At about this time Rob was writing the Neutrino courses, and he wanted to write a completely minimal resource manager example, /dev/null. His main slide was, “All you have to do is provide read() and write() message handlers, and you have a complete /dev/null!” I took that as a personal challenge, and removed even that requirement — the resource manager library now implements /dev/null in about half a dozen function calls. Since this library is shipped with Neutrino, everyone can write fully POSIX-compatible device drivers with minimal effort.
While the resource manager concept was significant in the evolution of Neutrino, and would indeed provide a solid base for the operating system, the fledgling OS needed more. Filesystems, connectivity (such as TCP/IP) and common devices (serial, console) were all being developed in parallel. After a lot of work, with lots of long hours, Neutrino 1.00 was released in early 1996. Over the next few years, more and more R&D staff were working on Neutrino. We've added SMP support, multiplatform support (x86, PowerPC and MIPS currently, with more to come), and the dispatch interface (that allows combining resource managers and other IPC methods), all covered in this book.
In August of 1999, we released QNX Neutrino 2.00; just in time for Rob's book! :-)
I think this book will be a “must have” for anyone who is writing programs for Neutrino.
Peter van der Veen
On a plane somewhere between Ottawa and San Jose
September 1999