Base API
Core services
Physical memory
The RAM service provides access to physical memory. Each RAM session corresponds to a memory pool with a bounded quota. From this pool, the RAM-session client can allocate memory blocks in the form of dataspaces.
Read-only memory modules
The ROM service provides access to boot-time present binary files such as files loaded by the boot loader or files contained in a non-volatile ROM area. Each ROM session corresponds to an open file. The file's content is made available to the client as a read-only dataspace.
Address-space-layout managment
The region-manager (RM) service manages address-space layouts. A RM session corresponds to an address space layout into which dataspaces can be placed. Hence, a RM session populated with dataspaces provides a generic abstraction for a physical page table referencing memory pages.
Protection domains
The protection-domain (PD) service enables the creation of address spaces that are isolated from each other. Each PD session corresponds to a protection domain. The PD service is rarely used by applications directly. Instead, Genode's process-creation framework uses this service internally.
CPU
The CPU service provides a facility for creating and managing threads. A CPU session corresponds to a CPU time allocator from which multiple threads can be allocated.
Device access
Interrupts
The IRQ service enables user-level device drivers to serve device interrupts. Each IRQ session corresponds to an associated interrupt line.
Memory-mapped I/O
The IO_MEM service enables user-level device drivers to obtain memory-mapped device resources as dataspaces. Each IO_MEM session corresponds to the reservation of a physical address range for which a dataspace is provided to the client. The user-level device driver can make the device resource visible in its address space by attaching the dataspace to its own RM session.
Port I/O
The IO_PORT service provides access to device IO-ports via an RPC interface. Each IO_PORT session corresponds to the access right to a port range.
Debug output
For low-level debugging, core provides a simple LOG service, which enables clients to print textual messages. In the LOG output, each message is tagged with the label of the corresponding client.
Runtime environment
Initial execution environment
A process is a composition of a protection domain (PD session), a memory pool (RAM session), an address-space layout (RM session), and a CPU session from which the main thread is created. These sessions form the environment of the process, which is represented by the Env class:
In addition to the process' initial sessions, the environment contains the heap of the process. The heap can be used for anonymous memory allocation and is the front end for allocating RAM dataspaces from the process' RAM session and attaching these dataspaces to the process' RM session. By default, the process environment holds one initial LOG session used as the standard text-output facility. For debugging purposes, there exists a printf front end for writing text to the initial LOG session.Interaction with the outside world
At creation time, the only communication partner of a process is its immediate parent process, which provides the following interface:
Data types and structures
Basic data types
Genode provides common integer types in its namespace. Integer types that can be derived from built-in compiler types are defined in:
In addition, there exist definitions for fixed-width integer types. For 32-bit platforms, those are defined in:Genode facilitates the use of exceptions to signal errors but it uses exception types only as textual expression of error code and for grouping errors. Normally, exceptions do not carry payload. For code consistency, exception types should inherit from the Exception base class.
For parsing structured text such as argument strings or XML, Genode provides simple tokenizing support via the Token class.
Structured data types
Most book-keeping tasks in Genode rely on single-connected lists, which use the List template.
For use cases where associative arrays are needed such as allocators and object pools, Genode relies on AVL trees. Furthermore, there exists a specialized version of the AVL tree for managing pools of strings. Because the List data type inserts new list elements at the list head, it cannot be used for implementing wait queues requiring first-in-first-out semantics. For such use cases, there exists a dedicated Fifo template. For representing an array of bits in a more memory-efficient manner than using an array of bool values, a Bit_array can be used. For allocating indices within a fixed-size range of numbers, a bit allocator is a suitable tool. It is meant to be used for managing ID name spaces.Argument strings
Genode uses comma-separated sequences of tag=value assignments for specifying session-construction arguments. The Arg_string provides an interface for extracting individual values from argument strings and for argument-string manipulation.
Allocators
All allocators implement the generic Allocator interface. Allocators that operate on address ranges supplement the plain Allocator by implementing the more specific Range_allocator interface.
The Slab allocator is tailored for allocating small fixed-size memory blocks from a big chunk of memory. For the common use case of using slab allocation for a certain type rather than for a known byte-size, there exists a typed slab allocator as a front-end of Slab. In contrast to the rather limited slab allocators, Allocator_avl allows for arbitrary allocations from a list of address regions. It implements a best-fit allocation strategy, supports arbitrary alignments, and allocations at specified addresses.Genode::Empty |
Inter-process communication
Remote procedure calls
Capability representation
Inter-process communication on Genode is based on capabilities. A capability is a system-wide unique object identity that can be passed between processes. On kernels with support for local names, a capability may have a different name in each process but still refer to the same object.
Each capability is associated with the type of the RPC interface the capability refers to - similar to how a C++ reference refers to the type of a specific C++ object.RPC marshalling and message transfer
Genode provides a mechanisms for implementing remote procedure calls (RPC). The interaction between the client (the caller) and the server (callee) consists of the following steps:
-
Client marshals arguments to form a message. The first part of the message is a function opcode.
-
Client sends message via the kernel to the server and blocks for the result.
-
Server dispatches request according to the opcode stored in the message.
-
Server unmarshalls function arguments,
-
Server executes the function,
-
Server marshals the produced results.
-
Server sends result message to the client.
-
Client unmarshals results.
An RPC interface is declared by annotating its abstract C++ class using the macros GENODE_RPC_FUNCTION and GENODE_RPC_INTERFACE.
Genode::Rpc_arg_in |
Genode::Rpc_arg_out |
Genode::Rpc_arg_inout |
Genode::Trait::Rpc_direction |
Genode::Trait::Call_return |
Genode::Trait::Exc_list |
Genode::Rpc_transfer_size |
Genode::Rpc_args_size |
Genode::Rpc_retval_size |
Genode::Rpc_msg_payload_size |
Genode::Rpc_function_list_msg_size |
Genode::Rpc_interface_msg_size |
Genode::Rpc_interface_is_inherited |
Genode::Cap_para_out |
Genode::Cap_return |
Genode::Rpc_caps_out |
Genode::Rpc_function_caps_out |
Asynchronous notifications
Inter-process communication via remote procedure calls requires both communication partners to operate in a synchronous fashion. The signalling framework complements the synchronous RPC mode of communication with an interface for issuing and receiving asynchronous notifications.
It defines interfaces for signal transmitters and signal receivers. A signal receiver can receive signals from multiple sources, whereas the sources of incoming signals are distinguishable. One or multiple threads can either poll or block for incoming signals. Each signal receiver is addressable via a capability. The signal transmitter provides fire-and-forget semantics for submitting signals to exactly one signal receiver. Signals are communicated in a reliable fashion, which means that the exact number of signals submitted to a signal transmitter are communicated to the corresponding signal receiver. Signals serve as raw notifications and cannot carry any payload.
Shared memory
Genode supports shared memory between multiple processes using dataspaces. Dataspaces are memory containers as provided by core's RAM, IO_MEM, or ROM services. Each dataspace is referenced by a capability that can be passed among multiple processes. Each process with the capability to a dataspace can access the dataspace's content by attaching the dataspace to its RM session. In addition to be used as arguments for RM-session calls, dataspaces provide the following interface:
Client-server framework
Server sessions
In the Genode architecture, servers provide their services over session-based communication channels. Each session provides an interface inherited from the Session base class.
Each service type is represented as a RPC object implementing the root interface. The server announces its service type by providing the service name and the capability of the service's root interface (announce function of the parent interface). Via the capability to the root interface, the parent is then able to create and destroy sessions. Because defining root interfaces for services follows a recurring pattern, there exists a default template that implements the standard behaviour of the root interface (Root_component).Connecting to services
The interaction of a client with a server involves the definition of session-construction arguments, the request of the session creation via its parent, the initialization of the matching RPC-client stub code with the received session capability, the actual use of the session interface, and the closure of the session. The Connection template provides a way to greatly simplify the handling of session arguments, session creation, and destruction on the client side. By implementing a service-specific connection class inherited from Connection, session arguments become plain constructor arguments, session functions can be called directly on the Connection object, and the session gets properly closed when destructing the Connection.
Threads and synchronization
Threads
On Genode, a thread is created by constructing an object of a class inherited from Thread. The new thread starts execution at the entry member function. With this design, each thread runs in the context of its object and can access context-specific information by accessing its member variables. Threads use a statically allocated stack, which is dimensioned as specified via a template argument.
There exists no timed sleep function in the Genode base framework. Timed sleep is instead be provided by a server implementing a timer-device driver. However, the special case of infinite blocking is covered by the following base API function. Genode allows all blocking operations of threads to be canceled via a cancel_blocking mechanism provided by core's CPU service. The cancellation of a blocking operation is reflected at the API level by a raised exception of the type Blocking_canceled.Synchronization
For mutual exclusive execution of critical sections, there exists a simple lock interface providing lock and unlock semantics. The lock comes in two flavours. Cancelable locks can be unblocked by force via core's cancel-blocking mechanism. In contrast, a non-cancelable lock (Lock) does not reflect the cancellation of its blocking operation at the API level but transparently re-enters its blocking state after a cancellation.
For the use case of using locks for protecting critical sections, the Lock_guard provides a convenient mechanism for the automated unlocking of a lock when leaving a variable scope. Alongside lock-based mutual exclusion of entering critical sections, organizing threads in a producer-consumer relationship is a common design pattern for thread synchronization. The Semaphore interface enables the implementation of this synchronization scheme. For managing object lifetimes in a way that prevents dangling pointers, there is a thread-safe weak-pointer mechanism.Child management
Child policy
For processes that manage a number of child processes, each child process is represented by an instance of the Child class. This instance contains the policy to be applied to the child (for example how session requests are routed to services) and contains the child's execution environment including the RAM session holding the child's memory quota.
The Service_pool classes support the management of services announced by children or provided locally, and the synchronization of service requests with service announcements.Process creation
The Process class provides a convenient front end for the procedure of creating a new process.