Overview

The Portable Operating System Interface (POSIX) is a family of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems. Zephyr implements a subset of the standard POSIX API specified by IEEE 1003.1-2017 (also known as POSIX-1.2017).

POSIX Support in Zephyr

POSIX support in Zephyr

Note

This page does not document Zephyr’s POSIX architecture, which is used to run Zephyr as a native application under the host operating system for prototyping, test, and diagnostic purposes.

With the POSIX support available in Zephyr, an existing POSIX conformant application can be ported to run on the Zephyr kernel, and therefore leverage Zephyr features and functionality. Additionally, a library designed to be POSIX conformant can be ported to Zephyr kernel based applications with no changes.

The POSIX API is an increasingly popular OSAL (operating system abstraction layer) for IoT and embedded applications, as can be seen in Zephyr, AWS:FreeRTOS, TI-RTOS, and NuttX.

Benefits of POSIX support in Zephyr include:

  • Offering a familiar API to non-embedded programmers, especially from Linux

  • Enabling reuse (portability) of existing libraries based on POSIX APIs

  • Providing an efficient API subset appropriate for small (MCU) embedded systems

POSIX Subprofiles

While Zephyr supports running multiple threads (possibly in an SMP configuration), as well as Virtual Memory and MMUs, Zephyr code and data normally share a common address space that is partitioned into separate Memory Domains. The Zephyr kernel executable code and the application executable code are typically compiled into the same binary artifact. From that perspective, Zephyr apps can be seen as running in the context of a single process by default; the optional POSIX_MULTI_PROCESS Option Group adds processes as kernel thread groups (see Multi-Process Design).

While multi-purpose operating systems (OS) offer full POSIX conformance, Real-Time Operating Systems (RTOS) such as Zephyr typically serve a fixed-purpose, have limited hardware resources, and experience limited user interaction. In such systems, full POSIX conformance can be impractical and unnecessary.

For that reason, POSIX defined the following Application Environment Profiles (AEP) as part of IEEE 1003.13-2003 (also known as POSIX.13-2003). Each AEP adds incrementally more features over the required POSIX System Interfaces.

POSIX Application Environment Profiles (AEP)

POSIX Application Environment Profiles (AEP)

  • Minimal Realtime System Profile (PSE51)

  • Realtime Controller System Profile (PSE52)

  • Dedicated Realtime System Profile (PSE53)

  • Multi-Purpose Realtime System (PSE54)

POSIX.13-2003 AEP were formalized in 2003 via “Units of Functionality” but the specification is now inactive (for reference only). Nevertheless, the intent is still captured as part of POSIX-1.2017 via Options and Option Groups.

For more information, please see IEEE 1003.1-2017, Section E, Subprofiling Considerations.

POSIX Applications in Zephyr

A POSIX app in Zephyr is built like any other app and therefore requires the usual prj.conf, CMakeLists.txt, and source code. For example, the app below leverages the nanosleep() and perror() POSIX functions.

prj.conf for a simple POSIX app in Zephyr
 CONFIG_POSIX_AEP_CHOICE_PSE51=y
A simple app that uses the POSIX API
 #include <stddef.h>
 #include <stdio.h>
 #include <time.h>

 void megasleep(size_t megaseconds)
 {
     struct timespec ts = {
         .tv_sec = megaseconds * 1000000,
         .tv_nsec = 0,
     };

     printf("See you in a while!\n");
     if (nanosleep(&ts, NULL) == -1) {
         perror("nanosleep");
     }
 }

 int main()
 {
     megasleep(42);
     return 0;
 }

For more examples of POSIX applications, please see the POSIX sample applications.

Configuration

Like most features in Zephyr, POSIX features are highly configurable but disabled by default. Users must explicitly choose to enable POSIX options via Kconfig selection.

Subprofiles

Applications should enable one of the Kconfig options below to quickly configure a pre-defined POSIX subprofile.

A subprofile enables its Option Groups and Options by default rather than forcing them on: each may be disabled individually, and further Option Groups may be enabled on top. A network appliance without processes, for example, is PSE52 plus networking:

CONFIG_POSIX_AEP_CHOICE_PSE52=y
CONFIG_POSIX_NETWORKING=y

The conformance macros (_POSIX_AEP_REALTIME_MINIMAL, _XOPEN_REALTIME and so on) are defined only when every constituent of the corresponding profile or Option Group is actually enabled, so a subtracted profile is reported honestly by <unistd.h> and sysconf().

Libraries should depend on CONFIG_POSIX_SYSTEM_INTERFACES and other POSIX Option Groups, as needed.

Further fine-tuning may be accomplished via additional POSIX-related Kconfig options.

Application conformance macros

IEEE 1003.1 reserves _POSIX_C_SOURCE and _XOPEN_SOURCE for the application: a source file states the version of the standard it is written to by defining one of them before it includes any header, and the headers then expose the matching set of declarations. The implementation never defines them. Zephyr and posix-next therefore set neither macro globally, and a library must not either; the global _POSIX_C_SOURCE=200809 of earlier releases was both the wrong value (the standard’s is 200809L) and a conflict for every application that defined the right one.

An application chooses its conformance level in its own build files or sources, for example

CMakeLists.txt: the whole application targets POSIX.1-2017
 target_compile_options(app PRIVATE -U_POSIX_C_SOURCE -D_POSIX_C_SOURCE=200809L)
or per source file, before the first #include
 #define _POSIX_C_SOURCE 200809L
 #include <time.h>

The -U first undefines the value some toolchains predefine, so the application’s definition is not reported as a redefinition. Defining neither macro is also a valid choice: each C library then applies its own default namespace. A library that needs a particular level defines it locally, on its own sources, with target_compile_definitions or zephyr_library_compile_definitions.

There is deliberately no Kconfig choice for the application conformance level: the macro belongs to a translation unit, not to the system configuration, and a Kconfig symbol would turn it back into a global definition for every library in the image. The implementation conformance macros that answer it (_POSIX_VERSION, _POSIX_THREADS, _POSIX_TIMERS and the other constants of <unistd.h>) are what Kconfig controls, and they report exactly the Option Groups that are enabled.

Legacy

Historically, Zephyr used CONFIG_POSIX_API to configure a set of POSIX features that was overloaded and always increasing in size.

The CONFIG_POSIX_API should be considered deprecated and will be removed. It shall not be used for new applications.

Applications should choose a POSIX subprofile mentioned above.