Implementation Details

In many ways, Zephyr provides support like any POSIX OS; API bindings are provided in the C programming language, POSIX headers are available in the standard include path, when configured.

Unlike other multi-purpose POSIX operating systems

  • Zephyr is not “a POSIX OS”. The Zephyr kernel was not designed around the POSIX standard, and POSIX support is an opt-in feature

  • Zephyr apps are not linked separately, nor do they execute as subprocesses

  • Zephyr, libraries, and application code are compiled and linked together, running similarly to a single-process application, in a single (possibly virtual) address space

  • Zephyr does not provide a POSIX shell, compiler, utilities, and is not self-hosting.

Note

Unlike the Linux kernel or FreeBSD, Zephyr does not maintain a static table of system call numbers for each supported architecture, but instead generates system calls dynamically at build time. See System Calls for more information.

Design

As a library, Zephyr’s POSIX API implementation makes an effort to be a thin abstraction layer between the application, middleware, and the Zephyr kernel.

Some general design considerations:

  • The POSIX interface and implementations should be part of Zephyr’s POSIX library, and not elsewhere, unless required both by the POSIX API implementation and some other feature. An example where the implementation should remain part of the POSIX implementation is getopt(). Examples where the implementation should be part of separate libraries are multithreading and networking.

  • When the POSIX API and another Zephyr subsystem both rely on a feature, the implementation of that feature should be as a separate Zephyr library that can be used by both the POSIX API and the other library or subsystem. This reduces the likelihood of dependency cycles in code. When practical, that rule should expand to include macros. In the example below, libposix depends on libzfoo for the implementation of some functionality “foo” in Zephyr. If libzfoo also depends on libposix, then there is a dependency cycle. The cycle can be removed via mutual dependency, libcommon.

digraph { node [shape=rect, style=rounded]; rankdir=LR; libposix [fillcolor="#d5e8d4"]; libzfoo [fillcolor="#dae8fc"]; libposix -> libzfoo; libzfoo -> libposix; }

Dependency cycle between POSIX and another Zephyr library

digraph { node [shape=rect, style=rounded]; rankdir=LR; libposix [fillcolor="#d5e8d4"]; libzfoo [fillcolor="#dae8fc"]; libcommon [fillcolor="#f8cecc"]; libposix -> libzfoo; libposix -> libcommon; libzfoo -> libcommon; }

Mutual dependencies between POSIX and other Zephyr libraries

  • POSIX API calls should be provided as regular callable C functions; if a Zephyr System Call is needed as part of the implementation, the declaration and the implementation of that system call should be hidden behind the POSIX API.