📜 ⬆️ ⬇️

QNX RTOS: A Bit of the Microkernel, Threads and Processes

Since my first short review of the QNX real-time operating system showed that there is interest among her residents of Habr, I decided to continue the cycle of notes. It seems to me that it is worth telling a little about the QNX6 system architecture. I think it is useful to define what a microkernel is and what tasks it solves. In the course of the story, two QNX-related myths will also be debunked. But first…

A little bit about POSIX


With the release of each new version of QNX (and it should be noted that the first version of the QNX RTOS is now back in 1981), the developers used the experience gained earlier and made the system better, including more convenient for developers. That is why QNX Neutrino supports POSIX 1003.1 standards, for example, thread management, real-time extensions (Realtime Extensions), Additional Realtime Extensions additional extensions, and Application Environment Profiles (AEP).

Why am I talking about this? Very simple. I would like to note two things that are characteristic of QNX and are related to POSIX. It is believed that any POSIX operating system hides in itself UNIX. And if so, then such a system is too cumbersome and cannot be used in embedded solutions. However, in the case of QNX, this is incorrect. The POSIX standard describes an interface, not an implementation. So, under the POSIX layer, anything can be hidden, including the microkernel.
')
The second point on which I would like to stop is probably obvious. These are, of course, the advantages that POSIX provides in QNX:
So it can be concluded that POSIX in QNX provides a number of advantages.

True core


The QNX RTOS is based on a microkernel architecture. I think it is worth stopping, and before going further, determine what a microkernel is (at least in QNX terminology). The term microkernel has become very popular for some time and many systems containing small cores are called microkernels. And if the nucleus is even smaller, then it is called the nano-core. This is not entirely true for QNX. Small core size is not the main goal. The idea is that many functions of the operating system (including such familiar ones as, for example, file system support) are moved from the kernel to the area of ​​user applications. And the microkernel itself performs only a small set of functions, most of which provide inter-task interaction:
Please note that even process management is not performed by the microkernel, but by the process administrator (who, it is true, is bundled with the microkernel in one module). Is this the true core of the system?

What I listed above is all that the QNX microkernel does. Things like network drivers or disk support are rendered into separate modules that run and work like normal user processes. With all the ensuing advantages (over systems based on a monolithic core). And what are the benefits? Very simple:

Myth: QNX microkernel written in assembly language


Some developers believe that the high performance and compactness of the QNX Neutrino microkernel is the result of what is written in assembler. But it is not. The microkernel is almost completely written in C. Productivity and compactness are consequences of using well-established algorithms and structures.

Processes and threads in QNX


According to the POSIX specification, the QNX RTOS supports streams. And even more, the QNX microkernel manages only threads, and it is the threads that can be considered the minimal “unit of execution”. Each process in QNX can contain one or more threads. In this way, the process can be considered as a container of threads.

In the simplest case, a process can (and should) contain one thread. But sometimes parallel execution of several algorithms in one process is required. I will give a small example from QNX. The file system manager can receive and process in parallel requests from several clients (other processes). If the manager were working in one stream, then the second client who approached him would have to wait for the completion of the previously requested operation. Fortunately, the file system manager in QNX runs in multiple threads, which allows you to serve several applications simultaneously and in parallel.

All processes in QNX are isolated from each other and run in their own virtual address space. The implementation of such protection is the responsibility of the memory management unit (Memory Management Unit, MMU). The presence of an MMU device in the processor is a prerequisite for running QNX. At the same time, threads of the same process work in the same address space.

There is a system implemented according to the scheme, when all tasks in the system are threads that operate in the same address space. On the one hand, it facilitates interprocess communication. But on the other hand, any error in any thread can lead to the collapse of the entire system. And to debug and maintain a system built on this principle is much more difficult.

Myth: QNX microkernel slowly switches contexts between processes


Initially, threads appeared on UNIX systems as a solution to the problem of too slow context switching between processes. Most likely, since then it is considered that the switching of contexts between processes occurs very slowly. And since using a microkernel and messaging, more context switches are performed than when using a monolithic kernel, a simple conclusion is made - QNX is slow. However, the QNX Neutrino architecture solves the context switching performance problem. And in QNX there is practically no difference in the speed of context switching between processes and threads.

How the stream lives


During the process, the threads in it can be created and deleted dynamically. When creating a thread, for example, using the pthread_create() function, the necessary resources are allocated and initialized, and the thread execution begins with the specified function in the address space of the process. When the thread is terminated, for example using the pthread_exit() function, resources are released.

While a thread is running, it can be in two states: ready (ready) or blocked (blocked). But in fact there are various reasons why a stream can be blocked and when displaying information about processes and threads using the pidin (a QNX-specific variant of the ps utility) we can observe various states, for example, SEND, RECEIVE, SIGWAITINFO, NANOSLEEP and others. For example, in this state, I now have USB manager threads:

 # pidin -P io-usb pid tid name prio STATE Blocked 4101 1 proc/boot/io-usb 10o SIGWAITINFO 4101 2 proc/boot/io-usb 21r RECEIVE 1 4101 3 proc/boot/io-usb 10o RECEIVE 4 4101 4 proc/boot/io-usb 10r NANOSLEEP 4101 5 proc/boot/io-usb 10o RECEIVE 4 

Here pid is the process ID in the system, tid is the thread ID in the process, prio is the priority number of the thread and the planning discipline, STATE is the state of the flow, Blocked is the value depends on the cause of the block. In the given example, threads 2, 3 and 5 are in the Receive-blocked state (i.e. are ready to receive messages from other threads).

Epilogue


I tried to talk about the benefits of POSIX and a bit about the role of the QNX microkernel. Hope this was interesting. In fact, it would be nice to tell you more about the planning disciplines and the mechanisms of inter-task interaction, but I thought that it was better to do this in separate topics so that all this information would not turn into a mess. I promise that next time will be more interesting.

And further. If someone is interested in QNX, then everything that I have described and even more can be read in the QNX Neutrino System Architecture. This documentation is available in electronic form on the website www.qnx.com (in English), and there is also a Russian translation in hard copy:
  1. QNX Neutrino Real-Time Operating System 6.3. System architecture ISBN 5-94157-827-X
  2. QNX Neutrino Real-Time Operating System 6.3. User's manual. ISBN 978-5-9775-0370-9

Source: https://habr.com/ru/post/125243/


All Articles