Rev 53 | Rev 56 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 53 | Rev 55 | ||
|---|---|---|---|
| Line 1... | Line 1... | ||
| 1 | <?xml version="1.0" encoding="UTF-8"?> |
1 | <?xml version="1.0" encoding="UTF-8"?> |
| - | 2 | <chapter id="time"> |
|
| - | 3 | <?dbhtml filename="time.html"?> |
|
| 2 | 4 | ||
| 3 | <chapter id="time"><?dbhtml filename="time.html"?> |
- | |
| 4 | <title>Time management</title> |
5 | <title>Time management</title> |
| 5 | - | ||
| 6 | <para> |
- | |
| 7 | Time is one of the dimensions in which kernel, as well as the whole system, operates. |
- | |
| 8 | It is of special importance to many kernel subsytems. Knowledge of time makes it possible |
- | |
| 9 | for the scheduler to preemptively plan threads for execution. Different parts of the kernel |
- | |
| 10 | can request execution of their callback function with some specified delay. A good example |
- | |
| 11 | of such kernel code is the synchronization subsystem which uses this functionality to implement |
- | |
| 12 | timeouting versions of synchronization primitives. |
- | |
| 13 | </para> |
- | |
| 14 | 6 | ||
| - | 7 | <para>Time is one of the dimensions in which kernel, as well as the whole |
|
| - | 8 | system, operates. It is of special importance to many kernel subsytems. |
|
| - | 9 | Knowledge of time makes it possible for the scheduler to preemptively plan |
|
| - | 10 | threads for execution. Different parts of the kernel can request execution |
|
| - | 11 | of their callback function with some specified delay. A good example of such |
|
| - | 12 | kernel code is the synchronization subsystem which uses this functionality |
|
| - | 13 | to implement timeouting versions of synchronization primitives.</para> |
|
| - | 14 | ||
| 15 | <section> |
15 | <section> |
| 16 | <title>System clock</title> |
16 | <title>System clock</title> |
| 17 | <para> |
- | |
| 18 | Every hardware architecture supported by HelenOS must support some kind of a device that can be |
- | |
| 19 | programmed to yield periodic time signals (i.e. clock interrupts). Some architectures have |
- | |
| 20 | external clock that is merely programmed by the kernel to interrupt the processor multiple |
- | |
| 21 | times in a second. This is the case of ia32 and amd64 architectures<footnote><para>When |
- | |
| 22 | running in uniprocessor mode.</para></footnote>, which use i8254 or a compatible chip to |
- | |
| 23 | achieve the goal. |
- | |
| 24 | </para> |
- | |
| 25 | <para> |
- | |
| 26 | Other architectures' processors typically contain two registers. The first |
- | |
| 27 | register is usually called a compare or a match register and can be set to an arbitrary value |
- | |
| 28 | by the operating system. The contents of the compare register then stays unaltered until it is |
- | |
| 29 | written by the kernel again. The second register, often called a counter register, can be also |
- | |
| 30 | written by the kernel, but the processor automatically increments it after every executed |
- | |
| 31 | instruction or in some fixed relation to processor speed. The point is that a clock interrupt is |
- | |
| 32 | generated whenever the values of the counter and the compare registers match. Sometimes, the scheme of |
- | |
| 33 | two registers is modified so that only one register is needed. Such a register, called a decrementer, |
- | |
| 34 | then counts towards zero and an interrupt is generated when zero is reached. |
- | |
| 35 | </para> |
- | |
| 36 | <para> |
- | |
| 37 | In any case, the initial value of the decrementer or the initial difference between the counter and |
- | |
| 38 | the compare registers, respectively, must be set accordingly to a known relation between the real time |
- | |
| 39 | and the speed of the decrementer or the counter register, respectively. |
- | |
| 40 | </para> |
- | |
| 41 | |
- | |
| 42 | </section> |
- | |
| 43 | 17 | ||
| - | 18 | <para>Every hardware architecture supported by HelenOS must support some |
|
| - | 19 | kind of a device that can be programmed to yield periodic time signals |
|
| - | 20 | (i.e. clock interrupts). Some architectures have external clock that is |
|
| - | 21 | merely programmed by the kernel to interrupt the processor multiple times |
|
| - | 22 | in a second. This is the case of ia32 and amd64 architectures<footnote> |
|
| - | 23 | <para>When running in uniprocessor mode.</para> |
|
| - | 24 | </footnote>, which use i8254 or a compatible chip to achieve the |
|
| - | 25 | goal.</para> |
|
| - | 26 | ||
| - | 27 | <para>Other architectures' processors typically contain two registers. The |
|
| - | 28 | first register is usually called a compare or a match register and can be |
|
| - | 29 | set to an arbitrary value by the operating system. The contents of the |
|
| - | 30 | compare register then stays unaltered until it is written by the kernel |
|
| - | 31 | again. The second register, often called a counter register, can be also |
|
| - | 32 | written by the kernel, but the processor automatically increments it after |
|
| - | 33 | every executed instruction or in some fixed relation to processor speed. |
|
| - | 34 | The point is that a clock interrupt is generated whenever the values of |
|
| - | 35 | the counter and the compare registers match. Sometimes, the scheme of two |
|
| - | 36 | registers is modified so that only one register is needed. Such a |
|
| - | 37 | register, called a decrementer, then counts towards zero and an interrupt |
|
| - | 38 | is generated when zero is reached.</para> |
|
| - | 39 | ||
| - | 40 | <para>In any case, the initial value of the decrementer or the initial |
|
| - | 41 | difference between the counter and the compare registers, respectively, |
|
| - | 42 | must be set accordingly to a known relation between the real time and the |
|
| - | 43 | speed of the decrementer or the counter register, respectively.</para> |
|
| - | 44 | ||
| - | 45 | <para>The rest of this section will, for the sake of clarity, focus on the |
|
| - | 46 | two-register scheme. The decrementer scheme is very similar.</para> |
|
| - | 47 | ||
| - | 48 | <para>The kernel must reinitialize the counter registers after each clock |
|
| - | 49 | interrupt in order to schedule next interrupt. However this step is tricky |
|
| - | 50 | and must be done with caution. Imagine that the clock interrupt is masked |
|
| - | 51 | either because the kernel is servicing another interrupt or because the |
|
| - | 52 | processor locally disabled interrupts for a while. If the clock interrupt |
|
| - | 53 | occurs during this period, it will be pending until interrupts are enabled |
|
| - | 54 | again. In theory, that could happen arbitrary counter register ticks |
|
| - | 55 | later. Which is worse, the ideal time period between two non-delayed clock |
|
| - | 56 | interrupts can also elapse arbitrary number of times before the delayed |
|
| - | 57 | interrupt gets serviced. The architecture-specific part of the clock |
|
| - | 58 | interrupt driver must avoid time drifts caused by this by taking proactive |
|
| - | 59 | counter-measures.</para> |
|
| - | 60 | ||
| - | 61 | <para>Let us assume that the kernel wants each clock interrupt be |
|
| - | 62 | generated every <constant>TICKCONST</constant> ticks. This value |
|
| - | 63 | represents the ideal number of ticks between two non-delayed clock |
|
| - | 64 | interrupts and has some known relation to real time. On each clock |
|
| - | 65 | interrupt, the kernel computes and writes down the expected value of the |
|
| - | 66 | counter register as it hopes to read it on the next clock interrupt. When |
|
| - | 67 | that interrupt comes, the kernel reads the counter register again and |
|
| - | 68 | compares it with the written down value. If the difference is smaller than |
|
| - | 69 | or equal to <constant>TICKCONST</constant>, then the time drift is none or |
|
| - | 70 | small and the next interrupt is scheduled earlier with a penalty of so |
|
| - | 71 | many ticks as is the value of the difference. However, if the difference |
|
| - | 72 | is bigger, then at least one clock signal was missed. In that case, the |
|
| - | 73 | missed clock signal is remembered in the special counter. If there are |
|
| - | 74 | more missed signals, each of them is recorded there. The next interrupt is |
|
| - | 75 | scheduled with respect to the difference similarily to the former case. |
|
| - | 76 | This time, the penalty is taken modulo <constant>TICKCONST</constant>. The |
|
| - | 77 | effect of missed clock signals is remedied in the generic clock interrupt |
|
| - | 78 | handler.</para> |
|
| - | 79 | </section> |
|
| - | 80 | ||
| - | 81 | <section> |
|
| - | 82 | <title>Timeouts</title> |
|
| - | 83 | ||
| - | 84 | <para>Kernel subsystems can register a callback function to be executed |
|
| - | 85 | with a specified delay. Such a registration is represented by a kernel |
|
| - | 86 | structure called <classname>timeout</classname>. Timeouts are registered |
|
| - | 87 | via <code>timeout_register</code> function. This function takes a pointer |
|
| - | 88 | to a timeout structure, a callback function, a parameter of the callback |
|
| - | 89 | function and a delay in microseconds as parameters. After the structure is |
|
| - | 90 | initialized with all these values, it is sorted into the processor's list |
|
| - | 91 | of active timeouts.Timeouts are sorted in this list according to the |
|
| - | 92 | number of clock interrupts remaining to their expiration and relative to |
|
| - | 93 | each other. </para> |
|
| - | 94 | ||
| - | 95 | <para>Timeouts can be unregistered via <code>timeout_unregister</code>. |
|
| - | 96 | This function can, as opposed to <code>timeout_register</code>, fail when |
|
| - | 97 | it is too late to remove the timeout from the list of active |
|
| - | 98 | timeouts.</para> |
|
| - | 99 | ||
| - | 100 | <para>Timeouts are nearing their expiration in the list of active timeouts |
|
| - | 101 | which exists on every processor in the system. The expiration counters are |
|
| - | 102 | decremented on each clock interrupt by the generic clock interrupt |
|
| - | 103 | handler. Due to the relative ordering of timeouts in the list, it is |
|
| - | 104 | sufficient to decrement expiration counter only of the first timeout in |
|
| - | 105 | the list. Timeouts with expiration counter equal to zero are removed from |
|
| - | 106 | the list and their callback function is called with respective |
|
| - | 107 | parameter.</para> |
|
| - | 108 | </section> |
|
| - | 109 | ||
| - | 110 | <section> |
|
| - | 111 | <title>Generic clock interrupt handler</title> |
|
| - | 112 | ||
| - | 113 | <para>On each clock interrupt, the architecture specific part of the clock |
|
| - | 114 | interrupt handler makes a call to the generic clock interrupt handler |
|
| - | 115 | implemented by the <code>clock</code> function. The generic handler takes |
|
| - | 116 | care of several mission critical goals:</para> |
|
| - | 117 | ||
| - | 118 | <itemizedlist> |
|
| - | 119 | <listitem> |
|
| - | 120 | <para>expiration of timeouts,</para> |
|
| - | 121 | </listitem> |
|
| - | 122 | ||
| - | 123 | <listitem> |
|
| - | 124 | <para>updating time of the day counters for userspace and</para> |
|
| - | 125 | </listitem> |
|
| - | 126 | ||
| - | 127 | <listitem> |
|
| - | 128 | <para>preemption of threads.</para> |
|
| - | 129 | </listitem> |
|
| - | 130 | </itemizedlist> |
|
| - | 131 | ||
| - | 132 | <para>The first two goals are performed exactly one more times than is the |
|
| - | 133 | number of missed clock signals (i.e. at least once and possibly more |
|
| - | 134 | times, depending on the missed clock signals counter). The remaining |
|
| - | 135 | timeslice of the running thread is decremented also with respect to this |
|
| - | 136 | counter. By considering its value, the kernel performs actions that would |
|
| - | 137 | otherwise be lost due to an occasional excessive time drift described in |
|
| - | 138 | previous paragraphs.</para> |
|
| - | 139 | </section> |
|
| 44 | </chapter> |
140 | </chapter> |
| 45 | 141 | ||