|Version 2 (modified by 12 years ago) ( diff ),
HelenOS as a Genode platform
This document is inteded to help understand the Portation of the Genode
Operating System Framework to HelenOS by pointing out major differences
in the design, implementation and used terms of both platforms and describing
how these differences has been solved.
Corresponding Ticket: #419
Code base: https://github.com/kurbel/genode/tree/base-spartan
# TODO: brief introduction to Genode in general
Before describing the design of the portation it is necessary to understand how both systems differ in their basic approaches:
|IPC End Points
| task to task communication
* fibrils used as userspace threads
* one answerbox per task
| thread to thread communication
* uses kernel primitives for threading
* calls are delivered to threads
| By global naming service (first task to be loaded in userspace)
* every new created task has an initial ipc connection to the naming service
* namimg service has to asked for at least first connection
* calls are being forwarded at will
| parent of each single task / thread identified by a unique parent capability
* new created tasks have a connection to its parent
* parent has to be asked for every connection
* call is routed through the task"tree" until destination is reached
|by phone handle (simple int value)
|by Capability, consisting of a platform specific argument and a global or local id for identifiaction
| different lenghts possible
* short messages using max 6 unsigned long arguments in a short call submitted directly
* longer messages submitted via copying memory from one task to another
* longer messages submitted via shared memory
* requesting or sharing a connection requires an extra ipc call for every single phone handle
* messages have user defined maximum length
* capabilities can be send within each message (more than one is possible)
From these differences arise several problems, which has to be solved by the portation:
Accessing the Answerbox:
For the fact, that there is only one single answerbox for every task but Genode requires single threads being the endpoints of the communication, the access to the answerbox has to regulated. As a thread can not determine whether an incoming call in the answerbox is addressed to it or not and taken calls from the answerbox can not be put back in, a solution has to be found how to take calls from the answerbox and deliver it to the addressed thread.
IPC destination vs. Capabilities
The purpose of using capabilities is to eliminate the knowledge of global names for destinations. Since an IPC call in Genode has to be delivered to a thread inside of a task, but the Spartan kernel itself can only deliver a call to a specific task, the addressed threads id has to be delivered within the call (as simple argument). This makes it inevitable that the capability has to have knowledge of the destinations thread id so the task then can deliver the call to the so addressed thread.
Sending Capabilities "within Messages"
The Spartan kernel can share IPC connections (phone handles) only by copying them via a special IPC call. For the API of Genode on the other side, sending Capabilities looks like the Capabilities are being send within the message (they are marshalled into the message using the "<<" operator like every other value). Capabilities in messages have to be recognized, stored, safely send and received.
To overcome those differences and problems, the portation of the IPC framework has been designed as followed:
Genode is using real kernel primitive threads for threading, created through a syscall.
IPC Manager Thread & IPC Call Queue:
Threads are divided into two groups: "worker threads" and "ipc manager threads". One single task may consist of an undefined (but at least one) amount of worker threads. Worker threads are created by the running application. Additionally every task consists of one single IPC Manager Thread, which has exclusive access to the tasks answerbox. The IPC Manager Thread loops the answerbox and as soon as a call drops in it delivers the call to the addressed thread. Every worker thread waiting for incoming calls has to register with the IPC Manager Thread, so calls can be delivered to the worker thread (since otherwise the IPC Manager Thread has no knowledge of other threads). Every worker thread owns a exclusive IPC Call queue. Waiting operations of the worker threads are performed on their IPC Call Queue and incoming calls are being stored in this queue by the IPC Manager Thread. The IPC Manager Thread is invokes as soon as one of the worker threads is waiting for an incoming ipc call to prevent unnecessary overhead.
Sending / Receiving IPC Messages:
Since all messages are of a variable size, a call which copies memory from the caller to the callee is used to send the message buffer (a char pointer with a defined amount of payload).
Sending Capabilities "within Messages":
When a Capability is marshalled into a message, it is stored in an extra queue with a fixed size (inside the message class). The number of Capabilities stored in this queue is being written at the very beginning of the message buffer right before it is being send (as described before), while the Capabilities themselves are not being touched. After sending the message buffer, all Capabilites are being send via an IPC call, cloning the phone handle of the Capability. Additionial arguments for every such call is the corresponding thread id where the phone handle points to and the global id of the Capability.