Operating System Abstraction Layer (OSAL)
1. Introduction
1.1 Purpose
The Operating System Abstraction Layer (OSAL) provides a portable interface to operating system services for the framework. It abstracts platform-specific OS functionality behind a common API, enabling F´ applications to run on multiple operating systems without modification to the source code.
1.2 Scope
This document describes the high-level architecture and design of the OSAL, including its abstraction patterns, supported features, and implementation strategy. It does not provide detailed API specifications or implementation details.
2. Core Services
Core services are those that directly wrap OS primitives and are implemented by each platform backend. They form the foundation upon which derived and generic services are built.
2.1 Concurrency
| Service | Purpose |
|---|---|
| Mutex | Mutual-exclusion lock and RAII ScopeLock helper. |
| Task | Thread creation, joining, and lifecycle management including start/stop callbacks. |
| Queue | Inter-task message passing with configurable depth, priority support, and blocking modes. |
2.2 File system
| Service | Purpose |
|---|---|
| File | Open, read, write, seek, flush, and CRC computation on files. |
| Directory | Open, iterate, and create directory entries. |
| FileSystem | Higher-level operations: remove, move/rename, copy, stat, query free space, and working-directory management. |
2.3 System Resources
| Service | Purpose |
|---|---|
| RawTime | Access to the system clock for measuring time intervals. |
| Cpu | CPU count and per-CPU usage statistics. |
| Memory | System memory usage statistics. |
2.4 Console
| Service | Purpose |
|---|---|
| Console | Write messages to the system console. |
3. Derived Services
Derived services are built on top of core services, providing higher-level abstractions that are implemented through the usage of core services.
| Service | Purpose | Core Services Used |
|---|---|---|
| ConditionVariable | Condition-variable signaling, paired with Mutex for producer/consumer patterns. | Mutex |
| ScopeLock | RAII helper for automatically acquiring and releasing a Mutex within a specific scope. | Mutex |
| IntervalTimer | Lightweight start/stop timer for measuring elapsed microseconds. | RawTime |
4. Generic Services
Generic services are implemented in the Os::Generic namespace and are not tied to platform-specific functionalities. They provide utilities or abstractions that are not tied to any specific OS, but are related to OS-level operations and are therefore included in the Os module.
| Service | Purpose |
|---|---|
| PriorityQueue | Heap-based priority queue implementation |
5. Implementation Architecture
5.1 Delegate Pattern
Every OSAL core service (e.g. File, Task, Mutex, etc.) follows a uniform three-layer pattern:
| Layer | Example | Description |
|---|---|---|
| Interface | Os::FileInterface |
A pure-virtual base class that defines the contract for a given OS service. |
| Wrapper | Os::File |
A final concrete class that holds a reference to the interface delegate constructed inside it. Application code interacts exclusively with the wrapper. |
| Implementation | Os::Posix::File |
A concrete class that implements the interface. This is what needs to be implemented to support a new OS on F Prime. |
This pattern allows for the selection of the implementation used for a given build to be performed through the build system at link-time: CMake chooses which Default*.cpp file to link, and that file provides the getDelegate factory function for a given OSAL implementation.
The following diagram (simplified for brevity) illustrates the relationship between these layers for the File service as an illustration. The same pattern applies to all other services. In the final Os::File wrapper, the OS functions (e.g. open(), read(), close()) are forwarded to its m_delegate which is a reference to an implementation of the Os::FileInterface, which is implemented by the platform-specific implementation class.
classDiagram
direction LR
namespace Os {
class FileInterface {
<<interface>>
+read()
}
class File {
<<final>>
-FileInterface& m_delegate
+read()
}
class PosixFile {
+read()
}
class FreeRTOSFile {
+read()
}
}
File *-- FileInterface
FileInterface <|-- PosixFile
FileInterface <|-- FreeRTOSFile
FileInterface <|-- File
5.2 API usage
Application code interacts exclusively with the wrapper classes (e.g., Os::File) and never directly with the interfaces or implementations. The wrapper forwards calls to the delegate interface, which is implemented by the platform-specific implementation class.
There are two usage patterns for the wrapper classes:
| Usage Pattern | Description | Service | Example Usage |
|---|---|---|---|
| Singletons | Global state and/or accessed through static methods | FileSystem, Cpu, Memory, Task | Trough a static call:Os::FileSystem::rename(source, destination); |
| Handles | Represents an OS object that carries state | File, Directory, Mutex, Task, Queue, Console | Through an instance:Os::File my_file; my_file.open("path/to/file.txt"); |
The full API for each service is documented in the header files in the Os/ module.
Note
Os::Task exposes both usage patterns. Os::Task::delay() is a static method that blocks the current task for a specified duration, while Os::Task instances represent individual tasks that can be started and stopped.
5.3 Initialization
Os::init() is called once at system startup. It initializes the singleton instances used by
services that have global state (Console, FileSystem, Cpu, Memory, Task).
While calling Os::init() is not strictly required, it does provide a deterministic place to initialize all singletons. When not called, singletons are initialized at the time of their first use.
5.4 Error Handling
All operations return a typed Status enumeration specific to each service. Platform
backends translate native error codes (e.g., errno) into these status values so that callers
never need to interpret platform-specific errors.
6. OSAL Implementations
The following OSAL implementations are available in the core F´ repository:
| Backend | Location | Description |
|---|---|---|
| Posix | Os/Posix/ |
Full implementation using POSIX APIs (pthread, file I/O, clock_gettime, etc.). Serves as the default for Linux, macOS, and other POSIX compliant systems. |
| Stub | Os/Stub/ |
No-op implementations that return Status::NOT_SUPPORTED. Useful while some implementations are not available to a platform. |
Additional OSAL implementations are available through platform support packages. For more information, see Supported Platforms or search for a platform support package in the fprime-community GitHub organization.
OSAL selection is controlled by the CMake build system. Each backend provides a set of Default<ServiceName>.cpp files; the build includes exactly one such file per service, giving the linker a single definition of each getDelegate factory method which initializes the wrapper's delegate reference to the appropriate implementation.
7. Resources
- How-To: Implement an OSAL — Step-by-step guide to implementing a new OSAL backend.
- Posix OSAL implementation — Reference implementation for POSIX systems.
- fprime-zephyr — OSAL implementation for Zephyr RTOS.
- fprime-vxworks - OSAL implementation for VxWorks RTOS.
- fprime-freertos - OSAL implementation for FreeRTOS.