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.
Communication endpoints
The capability (CAP) service enables the creation of globally
unique object identities (capabilities). Once allocated from
a CAP session, a capability can be associated with a local
object. A capability can be passed to another protection domain
via an inter-process communication (IPC) call whereby the
receiving protection domain obtains the right to send messages
to the associated object.
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.
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.
For safely using range allocators from multiple threads, synchronized versions
of range-allocator implementations can be constructed with the help of the
following template:
To protect the quantum of allocated memory from exceeding a predefined limit,
the following class can be used. It is used by server implementations that
subdivide their heap into client-specific partitions dimensioned according
to the resources donated by the respective client.
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.
base/include/base/rpc.h
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 |
The RPC API supports the use of basic types and compound types both as
values and references. For carrying more sophistic payload as RPC function
arguments or return values, the following RPC-specific types become handy:
The client-side API for performing synchronous RPC communication is
represented by the
Rpc_client class template accompanied with the
Capability class template defined in
base/capability.h.
On the server side, the RPC API consists of the
Rpc_object class template
and
Rpc_entrypoint class. An RPC entry point is a thread that manages a
number of RPC object and dispatches incoming RPC requests.
An RPC object is a C++ object that becomes remotely accessible once it is
associated with an RPC entry point (using the
manage function). At
association time, a capability for the RPC object is created, which can be
passed to other processes. The RPC object can then be invoked by everyone who
is in possession of this capability.
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.
base/include/base/signal.h
Genode::Signal |
Genode::Signal_context |
Genode::Signal_transmitter |
Genode::Signal_receiver |
Genode::Signal_dispatcher_base |
Genode::Signal_dispatcher |
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.
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.
base/include/base/service.h
Genode::Client |
Genode::Server |
Genode::Service |
Genode::Local_service |
Genode::Parent_service |
Genode::Child_service |
Genode::Service_registry |
Process creation
Utilities
Basic functions for handling strings and integers
ELF binary parser
Format-string handling