88,9 → 88,8 |
to a timeout structure, a callback function, a parameter of the callback |
function and a delay in microseconds as parameters. After the structure is |
initialized with all these values, it is sorted into the processor's list |
of active timeouts.Timeouts are sorted in this list according to the |
number of clock interrupts remaining to their expiration and relative to |
each other. </para> |
of active timeouts, according to the number of clock interrupts remaining |
to their expiration and relatively to already listed timeouts.</para> |
|
<para>Timeouts can be unregistered via <code>timeout_unregister</code>. |
This function can, as opposed to <code>timeout_register</code>, fail when |
129,12 → 128,73 |
</listitem> |
</itemizedlist> |
|
<para>The first two goals are performed exactly one more times than is the |
number of missed clock signals (i.e. at least once and possibly more |
times, depending on the missed clock signals counter). The remaining |
timeslice of the running thread is decremented also with respect to this |
counter. By considering its value, the kernel performs actions that would |
otherwise be lost due to an occasional excessive time drift described in |
previous paragraphs.</para> |
<para>The <code>clock</code> function checks for expired timeouts and |
decrements unexpired timeout expiration counters exactly one more times |
than is the number of missed clock signals (i.e. at least once and |
possibly more times, depending on the missed clock signals counter). The |
time of the day counters are also updated one more times than is the |
number of missed clock signals. And finally, the remaining timeslice of |
the running thread is decremented with respect to this counter as well. By |
considering its value, the kernel performs actions that would otherwise be |
lost due to an occasional excessive time drift described in previous |
paragraphs.</para> |
</section> |
|
<section> |
<title>Time source for userspace</title> |
|
<para>In HelenOS, userspace tasks don't communicate with the kernel in |
order to read system time. Instead, a mechanism that shares kernel time of |
the day counters with userspace address spaces is deployed. On the kernel |
side, during system initialization, HelenOS allocates a frame of physical |
memory and stores the time of the day counters there. The counters have |
the following structure:</para> |
|
<itemizedlist> |
<listitem> |
<para>first 32-bit counter for seconds,</para> |
</listitem> |
|
<listitem> |
<para>32-bit counter for microseconds and</para> |
</listitem> |
|
<listitem> |
<para>second 32-bit counter for seconds.</para> |
</listitem> |
</itemizedlist> |
|
<para>One of the userspace tasks with capabilities of memory manager (e.g. |
ns) asks the kernel to map this frame into its address space. Other, |
non-privileged, tasks then use IPC to communicate read-only sharing of |
this memory. Reading time in a userspace task is therefore just a matter |
of reading memory.</para> |
|
<para>There are two interesting points about this. First, the counters are |
32-bit even on 64-bit machines. The goal is to provide subsecond precision |
with the possibility to span roughly 136 years. Note that a single 64-bit |
microsecond counter could not be usually read atomically on 32-bit |
platforms. Now the second point is that 32-bit platforms cannot atomically |
read two 32-bit counters either. However, a generic protocol is used to |
guarantee that sequentially read times will create a non-decreasing |
sequence.</para> |
|
<para>The problematic part is updating both seconds and microseconds once |
in a second. Seconds must be incremented and microseconds must be reset. |
However, without any synchronization, the two kernel stores and the two |
userspace reads can arbitrarily interleave. Furthemore, the reader has no |
chance to detect that the counters were updated only from half. Therefore |
three counters are used in HelenOS.</para> |
|
<para>If seconds need to be updated, the kernel increments the first |
second counter, issues a write memory barrier operation, updates the |
microsecond counter, issues another write memory barrier operation and |
increments the second second counter. When only microseconds need to be |
updated, no special action is taken by the kernel. On the other hand, the |
userspace task must always read all three counters and in reversed order. |
A read memory barrier operation must be issued between each two reads. A |
non-atomic read is detected when the two second counters differ. The |
userspace library solves this situation by returning zero instead of the |
value read from the microsecond counter.</para> |
</section> |
</chapter> |