Rev 38 | Rev 99 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 38 | Rev 85 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | <?xml version="1.0" encoding="UTF-8"?> |
1 | <?xml version="1.0" encoding="UTF-8"?> |
- | 2 | <chapter id="ipc"> |
|
- | 3 | <?dbhtml filename="ipc.html"?> |
|
2 | 4 | ||
3 | <chapter id="ipc"><?dbhtml filename="ipc.html"?> |
- | |
4 | <title>IPC</title> |
5 | <title>IPC</title> |
5 | 6 | ||
6 | <para> |
- | |
7 | Due to the high intertask communication traffic, IPC becomes critical subsystem for microkernels, putting high demands on the |
7 | <para>Due to the high intertask communication traffic, IPC becomes critical |
- | 8 | subsystem for microkernels, putting high demands on the speed, latency and |
|
- | 9 | reliability of IPC model and implementation. Although theoretically the use |
|
- | 10 | of asynchronous messaging system looks promising, it is not often |
|
- | 11 | implemented because of a problematic implementation of end user |
|
- | 12 | applications. HelenOS implements a fully asynchronous messaging system but |
|
8 | speed, latency and reliability of IPC model and implementation. |
13 | with a special layer providing a user application developer a reasonably |
- | 14 | synchronous multithreaded environment sufficient to develop complex |
|
- | 15 | protocols.</para> |
|
9 | 16 | ||
10 | </para> |
17 | <section> |
11 | </chapter> |
18 | <title>Services provided by kernel</title> |
12 | 19 | ||
- | 20 | <para>Every message consists of 4 numeric arguments (32-bit and 64-bit on |
|
- | 21 | the corresponding platforms), from which the first one is considered a |
|
- | 22 | method number on message receipt and a return value on answer receipt. The |
|
- | 23 | received message contains identification of the incoming connection, so |
|
- | 24 | that it can distinguish the messages between different senders. Internally |
|
- | 25 | the message contains pointer to the originating task and to the source of |
|
- | 26 | the communication channel. If the message is forwarded, the originating |
|
- | 27 | task identifies the recipient of the answer, the source channel identifies |
|
- | 28 | connection in case of a hangup message.</para> |
|
- | 29 | ||
- | 30 | <para>Every message must be eventually answered. The system keeps track of |
|
- | 31 | all messages, so that it can answer them with appropriate error code |
|
- | 32 | should one of the connection parties fail unexpectedly. To limit buffering |
|
- | 33 | of messages in the kernel, every process is limited in a number of |
|
- | 34 | asynchronous messages it may have unanswered simultanously. If the limit |
|
- | 35 | is reached, the kernel refuses to send any other message, until some of |
|
- | 36 | the active messages are answered.</para> |
|
- | 37 | ||
- | 38 | <section> |
|
- | 39 | <title>Low level IPC</title> |
|
- | 40 | ||
- | 41 | <para>The whole IPC subsystem consists of one-way communication |
|
- | 42 | channels. Each task has one associated message queue (answerbox). The |
|
- | 43 | task can open connections (identified by phone id) to other tasks, send |
|
- | 44 | and forward messages through these connections and answer received |
|
- | 45 | messages. Every sent message is identified by a unique number, so that |
|
- | 46 | the response can be later matched against it. The message is sent over |
|
- | 47 | the phone to the target answerbox. Server application periodically |
|
- | 48 | checks the answerbox and pulls messages from several queues associated |
|
- | 49 | with it. After completing the requested action, server sends a reply |
|
- | 50 | back to the answerbox of the originating task. </para> |
|
- | 51 | ||
- | 52 | <para>If a need arises, it is possible to <emphasis>forward</emphasis> a |
|
- | 53 | recevied message throught any of the open phones to another task. This |
|
- | 54 | mechanism is used e.g. for opening new connections.</para> |
|
- | 55 | </section> |
|
- | 56 | ||
- | 57 | <section> |
|
- | 58 | <title>Services for user application</title> |
|
- | 59 | ||
- | 60 | <para>On top of this simple protocol the kernel provides special |
|
- | 61 | services including opening new connection to other tasks, offering |
|
- | 62 | callback connections and sending and receiving address space areas. |
|
- | 63 | </para> |
|
- | 64 | </section> |
|
- | 65 | </section> |
|
- | 66 | ||
- | 67 | <section> |
|
- | 68 | <title>Userspace view</title> |
|
- | 69 | ||
- | 70 | <para>The conventional design of the asynchronous api seems to produce |
|
- | 71 | applications with one event loop and several big switch statements. |
|
- | 72 | However, by intensive utilization of user-space threads, it was possible |
|
- | 73 | to create an environment that is not necesarilly restricted to this type |
|
- | 74 | of event-driven programming and allows for more fluent expression of |
|
- | 75 | application programs. </para> |
|
- | 76 | ||
- | 77 | <section> |
|
- | 78 | <title>Single point of entry</title> |
|
- | 79 | ||
- | 80 | <para>Each tasks is associated with only one answerbox. If a |
|
- | 81 | multi-threaded application needs to communicate, it must be not only |
|
- | 82 | able to send a message, but it should be able to retrieve the answer as |
|
- | 83 | well. If several threads pull messages from task answerbox, it is a |
|
- | 84 | matter of fortune, which thread receives which message. If a particular |
|
- | 85 | thread needs to wait for a message answer, an idle |
|
- | 86 | <emphasis>manager</emphasis> task is found or a new one is created and |
|
- | 87 | control is transfered to this manager task. The manager tasks pops |
|
- | 88 | messages from the answerbox and puts them into appropriate queues of |
|
- | 89 | running tasks. If a task waiting for a message is not running, the |
|
- | 90 | control is transferred to it. </para> |
|
- | 91 | ||
- | 92 | <para>Very similar situation arises when a task decides to send a lot of |
|
- | 93 | messages and reaches kernel limit of asynchronous messages. In such |
|
- | 94 | situation 2 remedies are available - the userspace liberary can either |
|
- | 95 | cache the message locally and resend the message when some answers |
|
- | 96 | arrive, or it can block the thread and let it go on only after the |
|
- | 97 | message is finally sent to the kernel layer. With one exception HelenOS |
|
- | 98 | uses the second approach - when the kernel responds that maximum limit |
|
- | 99 | of asynchronous messages was reached, control is transferred to manager |
|
- | 100 | thread. The manager thread then handles incoming replies and when space |
|
- | 101 | is available, sends the message to kernel and resumes application thread |
|
- | 102 | execution.</para> |
|
- | 103 | ||
- | 104 | <para>If a kernel notification is received, the servicing procedure is |
|
- | 105 | run in the context of the manager thread. Although it wouldn't be |
|
- | 106 | impossible to allow recursive calling, it could potentially lead to an |
|
- | 107 | explosion of manager threads. Thus, the kernel notification procedures |
|
- | 108 | are not allowed to wait for a message result, they can only answer |
|
- | 109 | messages and send new ones without waiting for their results. If the |
|
- | 110 | kernel limit for outgoing messages is reached, the data is automatically |
|
- | 111 | cached within the application. This behaviour is enforced automatically |
|
- | 112 | and the decision making is hidden from developers view.</para> |
|
- | 113 | </section> |
|
- | 114 | ||
- | 115 | <section> |
|
- | 116 | <title>Synchronization problem</title> |
|
- | 117 | ||
- | 118 | <para>Unfortunately, in the real world is is never so easy. E.g. if a |
|
- | 119 | server handles incoming requests and as a part of it's response sends |
|
- | 120 | asynchronous messages, it can be easily prempted and other thread may |
|
- | 121 | start intervening. This can happen even if the application utilizes only |
|
- | 122 | 1 kernel thread. Classical synchronization using semaphores is not |
|
- | 123 | possible, as locking on them would block the thread completely and the |
|
- | 124 | answer couldn't be ever processed. The IPC framework allows a developer |
|
- | 125 | to specify, that the thread should not be preempted to any other thread |
|
- | 126 | (except notification handlers) while still being able to queue messages |
|
- | 127 | belonging to other threads and regain control when the answer arrives. |
|
- | 128 | </para> |
|
- | 129 | ||
- | 130 | <para>This mechanism works transparently in multithreaded environment, |
|
- | 131 | where classical locking mechanism (futexes) should be used. The IPC |
|
- | 132 | framework ensures that there will always be enough free threads to |
|
- | 133 | handle the threads requiring correct synchronization and allow the |
|
- | 134 | application to run more user-space threads inside the kernel threads |
|
- | 135 | without the danger of locking all kernel threads in futexes.</para> |
|
- | 136 | </section> |
|
- | 137 | ||
- | 138 | <section> |
|
- | 139 | <title>The interface</title> |
|
- | 140 | ||
- | 141 | <para></para> |
|
- | 142 | </section> |
|
- | 143 | </section> |
|
- | 144 | </chapter> |
|
13 | 145 |