17,8 → 17,8 |
<section> |
<title>Kernel Services</title> |
|
<para>Every message consists of four numeric arguments (32-bit and 64-bit on |
the corresponding platforms), from which the first one is considered a |
<para>Every message consists of four numeric arguments (32-bit and 64-bit |
on the corresponding platforms), from which the first one is considered a |
method number on message receipt and a return value on answer receipt. The |
received message contains identification of the incoming connection, so |
that the receiving application can distinguish the messages between |
101,11 → 101,11 |
|
<para>The communication between task A, that is connected to task B |
looks as follows: task A sends a message over its phone to the target |
asnwerbox. The message is saved in task B's incoming call queue. When task |
B fetches the message for processing, it is automatically moved into the |
dispatched call queue. After the server decides to answer the message, |
it is removed from dispatched queue and the result is moved into the |
answer queue of task A.</para> |
asnwerbox. The message is saved in task B's incoming call queue. When |
task B fetches the message for processing, it is automatically moved |
into the dispatched call queue. After the server decides to answer the |
message, it is removed from dispatched queue and the result is moved |
into the answer queue of task A.</para> |
|
<para>The arguments contained in the message are completely arbitrary |
and decided by the user. The low level part of kernel IPC fills in |
119,8 → 119,8 |
<para>Closing an incoming connection is done by responding to any |
incoming message with an EHANGUP error code. The connection is then |
immediately closed. The client connection identification (phone id) is |
not reused, until the client closes its own side of the |
connection ("hangs his phone up").</para> |
not reused, until the client closes its own side of the connection |
("hangs his phone up").</para> |
|
<para>When a task dies (whether voluntarily or by being killed), cleanup |
process is started.</para> |
157,8 → 157,9 |
<para>On top of this simple protocol the kernel provides special |
services closely related to the inter-process communication. A range of |
method numbers is allocated and protocol is defined for these functions. |
These messages are interpreted by the kernel layer and appropriate actions |
are taken depending on the parameters of the message and the answer.</para> |
These messages are interpreted by the kernel layer and appropriate |
actions are taken depending on the parameters of the message and the |
answer.</para> |
|
<para>The kernel provides the following services:</para> |
|
182,24 → 183,26 |
|
<para>On startup, every task is automatically connected to a |
<emphasis>naming service task</emphasis>, which provides a switchboard |
functionality. In order to open a new outgoing connection, the client sends a |
<constant>CONNECT_ME_TO</constant> message using any of his phones. If |
the recepient of this message answers with an accepting answer, a new |
connection is created. In itself, this mechanism would allow only |
duplicating existing connection. However, if the message is forwarded, |
the new connection is made to the final recipient.</para> |
functionality. In order to open a new outgoing connection, the client |
sends a <constant>CONNECT_ME_TO</constant> message using any of his |
phones. If the recepient of this message answers with an accepting |
answer, a new connection is created. In itself, this mechanism would |
allow only duplicating existing connection. However, if the message is |
forwarded, the new connection is made to the final recipient.</para> |
|
<para>In order for a task to be able to forward a message, it |
must have a phone connected to the destination task. |
The destination task establishes such connection by sending the <constant>CONNECT_TO_ME</constant> |
message to the forwarding task. A callback connection is opened afterwards. |
Every service that wants to receive connections |
has to ask the naming service to create the callback connection via this mechanism.</para> |
<para>In order for a task to be able to forward a message, it must have |
a phone connected to the destination task. The destination task |
establishes such connection by sending the |
<constant>CONNECT_TO_ME</constant> message to the forwarding task. A |
callback connection is opened afterwards. Every service that wants to |
receive connections has to ask the naming service to create the callback |
connection via this mechanism.</para> |
|
<para>Tasks can share their address space areas using IPC messages. The |
two message types - <constant>AS_AREA_SEND</constant> and <constant>AS_AREA_RECV</constant> are used for sending and |
receiving an address space area respectively. The shared area can be accessed |
as soon as the message is acknowledged.</para> |
two message types - <constant>AS_AREA_SEND</constant> and |
<constant>AS_AREA_RECV</constant> are used for sending and receiving an |
address space area respectively. The shared area can be accessed as soon |
as the message is acknowledged.</para> |
</section> |
</section> |
|
208,9 → 211,9 |
|
<para>The conventional design of the asynchronous API seems to produce |
applications with one event loop and several big switch statements. |
However, by intensive utilization of userspace pseudo threads, it was possible |
to create an environment that is not necessarily restricted to this type |
of event-driven programming and allows for more fluent expression of |
However, by intensive utilization of userspace fibrils, it was possible to |
create an environment that is not necessarily restricted to this type of |
event-driven programming and allows for more fluent expression of |
application programs.</para> |
|
<section> |
217,19 → 220,20 |
<title>Single Point of Entry</title> |
|
<para>Each task is associated with only one answerbox. If a |
multithreaded application needs to communicate, it must be not only |
able to send a message, but it should be able to retrieve the answer as |
well. If several pseudo threads pull messages from task answerbox, it is a |
matter of coincidence, which thread receives which message. If a particular |
thread needs to wait for a message answer, an idle |
<emphasis>manager</emphasis> pseudo thread is found or a new one is created and |
control is transfered to this manager thread. The manager threads pop |
messages from the answerbox and put them into appropriate queues of |
running threads. If a pseudo thread waiting for a message is not running, the |
control is transferred to it.</para> |
multithreaded application needs to communicate, it must be not only able |
to send a message, but it should be able to retrieve the answer as well. |
If several fibrils pull messages from task answerbox, it is a matter of |
coincidence, which fibril receives which message. If a particular fibril |
needs to wait for a message answer, an idle <emphasis>manager</emphasis> |
fibril is found or a new one is created and control is transfered to |
this manager fibril. The manager fibrils pop messages from the answerbox |
and put them into appropriate queues of running fibrils. If a fibril |
waiting for a message is not running, the control is transferred to |
it.</para> |
|
<figure float="1"> |
<title>Single point of entry</title> |
|
<mediaobject id="ipc2"> |
<imageobject role="pdf"> |
<imagedata fileref="images/ipc2.pdf" format="PDF" /> |
243,7 → 247,6 |
<imagedata fileref="images/ipc2.svg" format="SVG" /> |
</imageobject> |
</mediaobject> |
|
</figure> |
|
<para>Very similar situation arises when a task decides to send a lot of |
250,18 → 253,18 |
messages and reaches the kernel limit of asynchronous messages. In such |
situation, two remedies are available - the userspace library can either |
cache the message locally and resend the message when some answers |
arrive, or it can block the thread and let it go on only after the |
arrive, or it can block the fibril and let it go on only after the |
message is finally sent to the kernel layer. With one exception, HelenOS |
uses the second approach - when the kernel responds that the maximum limit |
of asynchronous messages was reached, the control is transferred to a manager |
pseudo thread. The manager thread then handles incoming replies and, when space |
is available, sends the message to the kernel and resumes the application thread |
execution.</para> |
uses the second approach - when the kernel responds that the maximum |
limit of asynchronous messages was reached, the control is transferred |
to a manager fibril. The manager fibril then handles incoming replies |
and, when space is available, sends the message to the kernel and |
resumes the application fibril execution.</para> |
|
<para>If a kernel notification is received, the servicing procedure is |
run in the context of the manager pseudo thread. Although it wouldn't be |
run in the context of the manager fibril. Although it wouldn't be |
impossible to allow recursive calling, it could potentially lead to an |
explosion of manager threads. Thus, the kernel notification procedures |
explosion of manager fibrils. Thus, the kernel notification procedures |
are not allowed to wait for a message result, they can only answer |
messages and send new ones without waiting for their results. If the |
kernel limit for outgoing messages is reached, the data is automatically |
270,6 → 273,7 |
|
<figure float="1"> |
<title>Single point of entry solution</title> |
|
<mediaobject id="ipc3"> |
<imageobject role="pdf"> |
<imagedata fileref="images/ipc3.pdf" format="PDF" /> |
283,7 → 287,6 |
<imagedata fileref="images/ipc3.svg" format="SVG" /> |
</imageobject> |
</mediaobject> |
|
</figure> |
</section> |
|
298,16 → 301,16 |
possible as locking on them would block the thread completely so that |
the answer couldn't be ever processed. The IPC framework allows a |
developer to specify, that part of the code should not be preempted by |
any other pseudo thread (except notification handlers) while still being able |
to queue messages belonging to other pseudo threads and regain control when the |
any other fibril (except notification handlers) while still being able |
to queue messages belonging to other fibrils and regain control when the |
answer arrives.</para> |
|
<para>This mechanism works transparently in multithreaded environment, |
where additional locking mechanism (futexes) should be used. The IPC |
framework ensures that there will always be enough free userspace threads |
to handle incoming answers and allow the application to run more |
pseudo threads inside the usrspace threads without the danger of |
locking all userspace threads in futexes.</para> |
framework ensures that there will always be enough free userspace |
threads to handle incoming answers and allow the application to run more |
fibrils inside the userspace threads without the danger of locking all |
userspace threads in futexes.</para> |
</section> |
|
<section> |
321,12 → 324,12 |
multithreaded environment.</para> |
|
<para>The server interface requires the developer to specify a |
<function>connection_thread</function> function. When new connection is |
detected, a new pseudo thread is automatically created and control is |
<function>connection_fibril</function> function. When new connection is |
detected, a new fibril is automatically created and control is |
transferred to this function. The code then decides whether to accept |
the connection and creates a normal event loop. The userspace IPC |
library ensures correct switching between several pseudo threads |
within the kernel environment.</para> |
library ensures correct switching between several threads within the |
kernel environment.</para> |
</section> |
</section> |
</chapter> |