SlideShare a Scribd company logo
1 of 37
Download to read offline
#3 - BarCamp Semihalf
System wbudowany? - Zrób to sam!
Jak napisać własny RTOS?
Radosław Biernacki
radoslaw.biernacki@gmail.com
Rozkład jazdy
1. Wstęp do tematu i motywacja
2. Czym jest zadanie?
3. Przełączanie kontekstu
4. Do czego służy scheduler?
5. Sekcje krytyczne systemu
6. Blokowanie i wznawianie zadań
7. Wywłaszczanie zadań
8. Mutex i inwersja priorytetów
Chętnie wyjaśnię wątpliwości, proszę o
zgłaszanie się w przypadku niejasności.
Nagrody za aktywność
Czym jest RTOS?
RTOS - ang. Real Time Operating System
RTOS vs inne systemy np Linux, Windows:
- nie dzielą czasu CPU proporcjonalnie do priorytetu zadania
- zawsze wykonuje najważniejsze zadanie kosztem pozostałych zadań
- deterministyczny czas reakcji na zdarzenia
- (zazwyczaj) nie obsługują pamięci wirtualnej
- zastosowania sterowanie procesami fizycznymi (rakiety, motoryzacja,
automatyka)
Po co pisać własny RTOS?
- bo nie jest to trudne!
- bo to dobre ćwiczenie z zakresu systemów operacyjnych
- sucha teoria vs praktyka
- git clone https://github.com/rosly/rados.git
Ale od czego zacząć? Czym jest zadanie?
Zadanie jest programem, który
używa procesora w taki
sposób, jak gdyby posiadało
go na wyłączność.
Iluzja równoległego wykonania
= okresowe „przełączenia”
procesora pomiędzy
zadaniami.
cpi r19, 0x9A ; 154
and r3, r1
subi r20, 0x3F ; 63
sbci r21, 0x1F ; 31
subi r21, 0x33 ; 51
subi r20, 0x25 ; 37
cpi r21, 0xA4 ; 164
cpi r18, 0x10 ; 16
cpi r18, 0xD0 ; 208
and r3, r13
ori r22, 0x9C ; 156
andi r23, 0x43 ; 67
ori r21, 0x9F ; 159
subi r23, 0xF3 ; 243
ori r22, 0xD5 ; 213
andi r23, 0x40 ; 64
ldd r18, Y+7 ; 0x07
ldd r19, Y+8 ; 0x08
add r24, r18
adc r25, r19
std Y+2, r25 ; 0x02
std Y+1, r24 ; 0x01
ldd r24, Y+1 ; 0x01
ldd r25, Y+2 ; 0x02
call 0xfc2 ; 0xfc2
std Y+4, r25 ; 0x04
std Y+3, r24 ; 0x03
ldd r24, Y+3 ; 0x03
ldd r25, Y+4 ; 0x04
sbiw r24, 0x02 ; 2
std Y+6, r25 ; 0x06
mov r18, r24
ldd r24, Y+7 ; 0x07
ldd r25, Y+8 ; 0x08
movw r30, r24
std Z+20, r18 ; 0x14
ldd r24, Y+5 ; 0x05
ldd r25, Y+6 ; 0x06
movw r30, r24
std Z+11, r1 ; 0x0b
std Z+10, r1 ; 0x0a
ldd r24, Y+5 ; 0x05
ldd r25, Y+6 ; 0x06
adiw r28, 0x09 ; 9
in r0, 0x3f ; 63
cli
out 0x3e, r29 ; 62
out 0x3f, r0 ; 63
out 0x3d, r28 ; 61
TCB - Task Control Blok = struktura zadania
39 typedef enum {
40 TASKSTATE_RUNNING = 0,
42 TASKSTATE_READY,
44 TASKSTATE_WAIT,
47 TASKSTATE_DESTROYED,
50 TASKSTATE_INVALID
52 } os_taskstate_t;
50 os_taskqueue_t ready_queue;
70 os_taskqueue_t task_queue;
cpi r19, 0x9A
and r3, r1
subi r20, 0x3F
sbci r21, 0x1F
subi r21, 0x33
subi r20, 0x25
cpi r21, 0xA4
cpi r18, 0x10
cpi r18, 0xD0
and r3, r13
ori r22, 0x9C
andi r23, 0x43
ori r21, 0x9F
subi r23, 0xF3
ori r22, 0xD5
andi r23, 0x40
45 os_task_t* task_current;
78 typedef struct {
...
81 arch_context_t ctx;
84 list_t list;
96 os_taskstate_t state;
...
} os_task_t;
Cykl życia zadania
RUNNING
WAITING
READY
DESTROYED
Przydzielenie
procesora
Oddanie
procesora
Oczekiwanie
na zdarzenieWybudzenie
Terminacja
ready_queue
task1
task2
task3
task4
sem->task_queue
task5
task6
mtx->task_queue
task7
*task_current = *task8
* gotowe do uruchomienia
ale jeszcze nie
wykonywane
* aktualnie wykonywane
* zadania uśpione /
oczekujące na zdarzenie
&task_idle
* nigdy nie może być
uśpione
Rozkład jazdy
1. Wstęp do tematu i motywacja
2. Czym jest zadanie?
3. Przełączanie kontekstu
4. Do czego służy scheduler?
5. Sekcje krytyczne systemu
6. Blokowanie i wznawianie zadań
7. Wywłaszczanie zadań
8. Mutex i inwersja priorytetów
Czy są pytania?
0x41
0x56
0xFF
0x12
0xAB
Czym jest kontekst zadania?
R0
R30 [ZL]
R31 [ZH]
R28 [YH]
R29 [YH]
R26 [XH]
R27 [XH]
R25
R1
SREG
PC
SPH SPL
cpi r19, 0x9A ; 154
and r3, r1
subi r20, 0x3F ; 63
sbci r21, 0x1F ; 31
subi r21, 0x33 ; 51
subi r20, 0x25 ; 37
cpi r21, 0xA4 ; 164
cpi r18, 0x10 ; 16
cpi r18, 0xD0 ; 208
and r3, r13
ori r22, 0x9C ; 156
andi r23, 0x43 ; 67
ori r21, 0x9F ; 159
subi r23, 0xF3 ; 243
ori r22, 0xD5 ; 213
andi r23, 0x40 ; 64
Rejestry
RAM
ROM
Co zrobić żeby zapisać to ->
Użyć innego stosu niż
<- ten
I skoczyć poza to
<- to
Zapisanie kontekstu do TCB
290 #define arch_contextstore_i()
291 __asm__ __volatile__ (
293 "push r16"
298 "in r16, __SREG__"
299 "sbr r16, 0x80"
300 "push r16"
306 "push r28"
307 "push r29"
310 "push r0"
311 "push r1"
… (z pominięciem r16, r28 i r29)
339 "push r31"
346 "in r28, __SP_L__"
347 "in r29, __SP_H__"
353 "lds r30, task_current"
354 "lds r31, task_current+1"
355 "st Z, r28"
356 "std Z+1, r29"
R0 - R31
PC
SP
TCB
starego
zadania
ret PC
R16
SREG
R28
R29
R0 - R15
R17 - R27
R30 - R31
SREG
task_current
SP
TCB
nowego
zadania
SP
Stos starego zadania
45 os_task_t* task_current;
78 typedef struct {
81 arch_context_t ctx;
84 list_t list;
96 os_taskstate_t state;
...
} os_task_t;
56 typedef struct {
57 uint16_t sp;
58 } arch_context_t;
Przywrócenie kontekstu z TCB
391 #define arch_contextrestore_i()
392 __asm__ __volatile__ (
402 "lds r30, task_current"
403 "lds r31, task_current+1"
404 "ld r16, Z"
405 "ldd r17, Z+1"
406 "out __SP_L__, r16"
407 "out __SP_H__, r17"
410 "pop r31"
… (z pominięciem r16, r28 i r29)
438 "pop r1"
439 "pop r0"
440 "pop r29"
441 "pop r28"
447 "pop r16"
455 "out __SREG__, r16"
456 "pop r16"
459 "ret"
R0 - R31
PC
SP
TCB
starego
zadania
ret PC
R16
SREG
R28
R29
R0 - R15
R17 - R27
R30 - R31
SREG
task_current
SP
TCB
nowego
zadania
ret PC
R16
SREG
R28
R29
R0 - R15
R17 - R27
R30 - R31
SP
* skok do wcześniej przerwanego kodu
* podmiana wskaźnika zadania
Stos starego zadania Stos nowego zadania
całkiem nowy
45 os_task_t* task_current;
78 typedef struct {
81 arch_context_t ctx;
84 list_t list;
96 os_taskstate_t state;
...
} os_task_t;
56 typedef struct {
57 uint16_t sp;
58 } arch_context_t;
Jak inicjalizowany jest stos przy tworzeniu zadania?
204 void arch_task_init(os_task_t * task, void* stack_param,
205 size_t stack_size, os_taskproc_t proc,
206 void* param)
207 {
208 uint8_t *stack = ((uint8_t*)stack_param) + stack_size - 1;
209
211 *(stack--) = (uint8_t)((uint16_t)arch_task_start & 0xFF);
212 *(stack--) = (uint8_t)((uint16_t)arch_task_start >> 8);;
213 *(stack--) = 0; /* R16 */
214 *(stack--) = 1 << SREG_I;
...
252 *(stack--) = 0; /* R31 */
253
255 task->ctx.sp = (uint16_t)stack;
256 }
ret PC
R16
SREG
R28
R29
R0 - R15
R17 - R27
R30 - R31
Stos zadania
78 typedef struct {
81 arch_context_t ctx;
84 list_t list;
96 os_taskstate_t state;
...
} os_task_t;
56 typedef struct {
57 uint16_t sp;
58 } arch_context_t;
Rozkład jazdy
1. Wstęp do tematu i motywacja
2. Czym jest zadanie?
3. Przełączanie kontekstu
4. Do czego służy scheduler?
5. Sekcje krytyczne systemu
6. Blokowanie i wznawianie zadań
7. Wywłaszczanie zadań
8. Mutex i inwersja priorytetów
Czy są pytania?
Scheduler i priorytetyzacja zadań
Task1 hi prio
Task2
Task3
Task4 low prio
O(1) Enqueue any O(1) Dequeue top prio
with FIFO for equal prio
os_taskqueue_t ready_queue : READY -> RUNNING
254 os_task_t *task = os_taskqueue_dequeue(&ready_queue)
***
zły projekt = głodzenie
„fair schedule”
Enqueue ze złożonością O(1)
181 static inline void os_task_makeready(os_task_t *task)
182 {
183 task->state = TASKSTATE_READY;
184 os_taskqueue_enqueue(&ready_queue, task);
185 }
269 void OS_HOT os_taskqueue_enqueue(
270 os_taskqueue_t* task_queue,
271 os_task_t* task)
272 {
274 list_append(&(task_queue->tasks[task->prio_current]), &(task->list));
276
278 arch_bitmask_set(task_queue->mask, task->prio_current);
279 }
154 typedef struct os_taskqueue_tag {
157 list_t tasks[OS_CONFIG_PRIOCNT];
160 arch_bitmask_t mask;
162 } os_taskqueue_t;
1
0
1
1
Head list_t list_t
Head list_t
Head list_t list_t
Head
arch_bitmask_t
Dequeue ze złożonością O(1)
359 os_task_t* OS_HOT os_taskqueue_dequeue(
os_taskqueue_t* task_queue)
360 {
361 uint_fast8_t maxprio;
362
364 maxprio = arch_bitmask_fls(task_queue->mask);
365 if (0 == maxprio)
366 {
367 return NULL;
368 }
369 --maxprio; /* convert to index counted from 0 */
370
371 return os_taskqueue_intdequeue(task_queue, maxprio);
372 }
334 static os_task_t* os_taskqueue_intdequeue(
335 os_taskqueue_t *task_queue,
336 uint_fast8_t maxprio)
337 {
338 list_t *task_list;
339 os_task_t *task;
340
342 task_list = &task_queue->tasks[maxprio];
343 task = os_container_of(list_detachfirst(task_list), os_task_t, list);
344 if (list_is_empty(task_list))
345 {
347 arch_bitmask_clear(task_queue->mask, maxprio);
348 }
349
351 return task;
352 }
Dequeue ze złożonością O(1)
154 typedef struct os_taskqueue_tag {
157 list_t tasks[OS_CONFIG_PRIOCNT];
160 arch_bitmask_t mask;
162 } os_taskqueue_t;
1
0
1
1
Head list_t list_t
Head list_t
Head list_t list_t
Head
arch_bitmask_t
131 static inline uint_fast8_t arch_bitmask_fls(arch_bitmask_t bitfield)
132 {
133 return ((bitfield == 0)?
134 0 : ((sizeof(unsigned int) * 8) - __builtin_clz((unsigned int)bitfield)));
135 }
271 uint_fast8_t arch_bitmask_fls(arch_bitmask_t bitfield)
272 {
273 static const OS_PROGMEM uint8_t log2lkup[256] = {
274 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U...
275 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U...
...
289 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U...
290 };
291
292 return log2lkup[bitfield];
293 }
Blokowanie schedulera vs sekcje krytyczne
135 arch_criticalstate_t cristate;
136
138 arch_critical_enter(cristate);
...
144 arch_critical_exit(cristate);
- użytkownik może wyłączyć
przerwania na czas wywołań OS
- kod przerwań może obsługiwać
zagnieżdżanie (włączenie przerwań
przed powrotem)
62 volatile os_atomic_t isr_nesting = 0;
137 extern volatile os_atomic_t sched_lock;
...
219 static inline void os_scheduler_intlock(void)
220 {
221 os_atomic_inc(sched_lock);
222 }
223
224 static inline void os_scheduler_intunlock(bool sync)
225 {
226 os_atomic_dec(sched_lock);
227
228 if (!sync)
229 {
232 os_schedule(1);
233 }
234 }
0 - kontekst zadania
1 - kontekst przerwania
>1 - zagnieżdżone przerwanie
Rozkład jazdy
1. Wstęp do tematu i motywacja
2. Czym jest zadanie?
3. Przełączanie kontekstu
4. Do czego służy scheduler?
5. Sekcje krytyczne systemu
6. Blokowanie i wznawianie zadań
7. Wywłaszczanie zadań
8. Mutex i inwersja priorytetów
Czy są pytania?
Semafor - przypomnienie
83 void os_sem_create(os_sem_t* sem, os_atomic_t init_value);
109 void os_sem_destroy(os_sem_t* sem);
140 os_retcode_t OS_WARN_UNUSEDRET os_sem_down(
141 os_sem_t* sem,
142 uint_fast16_t timeout_ticks);
165 void os_sem_up_sync(os_sem_t* sem, bool sync);
54 typedef volatile arch_atomic_t os_atomic_t;
64 typedef struct os_sem_tag {
66 os_taskqueue_t task_queue;
69 os_atomic_t value;
71 } os_sem_t;
sem->value > 0
os_sem_up() nieblokujące
dowolne zadanie
os_sem_down()
uśpienie
wybudzenie
sem->task_queue
This is your last chance. After this, there is no turning back
Embedded ?
Everything runs in
cloud now ...
How deep the
rabbit hole goes ?
(4 strony listingów)
Kod os_sem_down()
78 os_retcode_t OS_WARN_UNUSEDRET os_sem_down(
79 os_sem_t* sem,
80 uint_fast16_t timeout_ticks)
81 {
82 os_retcode_t ret;
83 os_timer_t timer;
84 arch_criticalstate_t cristate;
85
86 OS_ASSERT(0 == isr_nesting); /* przerwanie ? */
87 OS_ASSERT(task_current == &task_idle);
89
92 arch_critical_enter(cristate); /* sekcja krytyczna */
93 do
94 {
95 if (sem->value > 0) /* potencjalna optymalizacja dla ARM */
96 {
102 --(sem->value);
103 ret = OS_OK;
104 break; /* po kłopocie */
105 }
108 if (OS_TIMEOUT_TRY == timeout_ticks)
109 {
111 ret = OS_WOULDBLOCK; /* musiał bym uśpić zadanie */
112 break;
113 }
114
116 if (OS_TIMEOUT_INFINITE != timeout_ticks) /* pominiemy detale */
117 {
119 os_blocktimer_create(&timer, os_sem_timerclbck, timeout_ticks);
120 }
121
123 os_task_block_switch(&(sem->task_queue), OS_TASKBLOCK_SEM);
124
127 os_blocktimer_destroy(task_current);
128
131 ret = task_current->block_code;
132
133 } while (0);
134 arch_critical_exit(cristate);
135
136 return ret;
137 }
powrót dopiero po wybudzeniu
dalej na następnym slajdzie
Wstrzymywanie zadania
515 void OS_HOT os_task_block_switch(
516 os_taskqueue_t* task_queue,
517 os_taskblock_t block_type)
518 {
520 os_task_makewait(task_queue, block_type);
524 arch_context_switch(os_taskqueue_dequeue(&ready_queue));
531 task_current->state = TASKSTATE_RUNNING;
532 }
187 static inline void os_task_makewait(
188 os_taskqueue_t *task_queue,
189 os_taskblock_t block_type)
190 {
193 task_current->state = TASKSTATE_WAIT;
194 task_current->block_type = block_type;
195 os_taskqueue_enqueue(task_queue, task_current);
196 }
Pseudo kod (oryginalnie ASM)
61 void OS_NAKED OS_HOT arch_context_switch(
os_task_t * new_task)
62 {
63 arch_contextstore(); /* podobna do już omawianej */
106 task_current = new_task; /* przemilczana poprzednio */
107 arch_contextrestore(); /* podobna do już omawianej */
108 }
skok przy powrocie
???
1
0
1
1
Head list_t list_t
Head list_t
Head list_t list_t
Head
arch_bitmask_t
gdyby ktoś zapomniał
sem->task_queue i ready_queue ->
&(sem->task_queue)
nigdy =NULL bo idle_task
Gdzie jesteśmy ?
83 void os_sem_create(os_sem_t* sem, os_atomic_t init_value);
109 void os_sem_destroy(os_sem_t* sem);
140 os_retcode_t OS_WARN_UNUSEDRET os_sem_down(
141 os_sem_t* sem,
142 uint_fast16_t timeout_ticks);
165 void os_sem_up_sync(os_sem_t* sem, bool sync);
54 typedef volatile arch_atomic_t os_atomic_t;
64 typedef struct os_sem_tag {
66 os_taskqueue_t task_queue;
69 os_atomic_t value;
71 } os_sem_t;
value > 0
os_sem_up() nieblokujące
dowolne zadanie
os_sem_down()
uśpienie
wybudzenie
sem->task_queue
Kod os_sem_up_sync()
147 void os_sem_up_sync(os_sem_t* sem, bool sync)
148 {
149 arch_criticalstate_t cristate;
150 os_task_t *task;
151
159 OS_ASSERT((isr_nesting == 0) || (sync == false));
161
162 arch_critical_enter(cristate);
163
165 OS_ASSERT(sem->value < (OS_ATOMIC_MAX - 1));
166
168 task = os_taskqueue_dequeue(&(sem->task_queue));
169 if (NULL == task) /* czy czeka jakies zadanie ? */
170 {
172 ++(sem->value); /* po kłopocie */
173 } else {
177 os_blocktimer_destroy(task);
178
179 task->block_code = OS_OK; /* powód wybudzenia */
180 os_task_makeready(task); /* tylko wybudzenie */
181
186 if (!sync) /* czy zapobiegać context switch */
187 {
190 os_schedule(1); /* potencjalne przydzielenie CPU */
191 }
192 }
193 arch_critical_exit(cristate);
194 }
181 static inline void os_task_makeready(os_task_t *task)
182 {
183 task->state = TASKSTATE_READY;
184 os_taskqueue_enqueue(&ready_queue, task);
185 }
Wznawianie zadań
467 void OS_HOT os_schedule(uint_fast8_t higher_prio)
468 {
469 os_task_t *new_task;
470
474 if (OS_LIKELY((isr_nesting <= 1) && (0 == sched_lock)))
475 {
478 new_task = os_taskqueue_dequeue_prio(
479 &ready_queue, task_current->prio_current + higher_prio);
480
482 if (NULL != new_task) /* może NULL, patrz higher_prio */
483 {
486 os_task_makeready(task_current); /* bieżące musi przejść do READY */
488 if (0 == isr_nesting) /* czy przerwanie ? */
489 {
490 arch_context_switch(new_task);
491 } else {
495 task_current = new_task; /* przerwanie!!… context switch na końcu ISR */
496 }
497 /* tu wrócimy dopiero po ponownym przydzieleniu CPU */
498 task_current->state = TASKSTATE_RUNNING;
499 }
500 }
501 }
181 static inline void os_task_makeready(os_task_t *task)
182 {
183 task->state = TASKSTATE_READY;
184 os_taskqueue_enqueue(&ready_queue, task);
185 }
Pseudo kod (oryginalnie ASM)
61 void OS_NAKED OS_HOT arch_context_switch(
os_task_t * new_task)
62 {
63 arch_contextstore();
106 task_current = new_task;
107 arch_contextrestore();
108 }
skok przy ret
???
Rozkład jazdy
1. Wstęp do tematu i motywacja
2. Czym jest zadanie?
3. Przełączanie kontekstu
4. Do czego służy scheduler?
5. Sekcje krytyczne systemu
6. Blokowanie i wznawianie zadań
7. Wywłaszczanie zadań
8. Mutex i inwersja priorytetów
Czy są pytania?
Preemption - Wywłaszczanie zadań
Zadanie nisko
priorytetowe
Przerwanie wybudzające
zadanie wysoko priorytetowe
Zadanie wysoko priorytetowe
Czas procesora
Przydział
procesora
czas reakcji na
zdarzenie
sekcja krytyczna
kernela
zgłoszenie przerwania
Zadanie nisko
priorytetowe
Przerwanie wybudzające
zadanie wysoko priorytetowe
Zadanie wysoko priorytetowe
Czas procesora
Przydział
procesora
czas reakcji na
zdarzenie
sekcja krytyczna
kernela
zgłoszenie przerwania
Cooperative multitasking
np Contiki
Preemptive multitasking
omawiany system
Przykład przerwania przez takt zegara systemowego
89 #define OS_ISR __attribute__((naked, signal, used, externally_visible))
183 void OS_ISR TIMER1_COMPA_vect(void)
184 {
185 arch_contextstore_i(tick);
186
189 os_tick(); /* potencjalne przełączenie task_current w os_sched() */
194
195 arch_contextrestore_i(tick); /* potencjalny context switch i skok przez reti*/
196 }
Kod przyjęcia i wyjścia z przerwania:
407 #define arch_contextrestore_i(_isrName)
408 __asm__ __volatile__ (
410 "cli" /* zagnieżdżone przerwania */
412 "lds r16, isr_nesting"
413 "dec r16"
414 "sts isr_nesting, r16"
416 "brne isr_contextrestore_nested_%="
418 "lds r30, task_current"
419 "lds r31, task_current+1"
420 "ld r16, Z"
421 "ldd r17, Z+1"
422 "out __SP_L__, r16"
423 "out __SP_H__, r17"
306 #define arch_contextstore_i(_isrName)
307 __asm__ __volatile__ (
309 "push r16"
...
355 "push r31"
357 "lds r16, isr_nesting"
358 "inc r16"
359 "sts isr_nesting, r16"
362 "in r28, __SP_L__"
363 "in r29, __SP_H__"
364 "eor r1, r1"
366 "cpi r16, 1"
367 "brne isr_contextstore_nested_%="
369 "lds r30, task_current" /* tylko jeśli nesting == 1 */
370 "lds r31, task_current+1"
371 "st Z, r28"
372 "std Z+1, r29"
373 "isr_contextstore_nested_%=:"
374 :: )
424 "isr_contextrestore_nested_%=:"
426 "pop r31"
...
457 "pop r28"
463 "pop r16"
469 "sbrc r16, 7" /* czy jest IE ? */
470 "rjmp isr_contextrestore_enableint_%="
471 "out __SREG__, r16" /* tu nie ma IE */
472 "pop r16"
475 "ret"
476 "isr_contextrestore_enableint_%=:"
481 "cbr r16, 0x80" /* jeszcze nie teraz */
482 "out __SREG__, r16" /* jedna instrukcja */
483 "pop r16"
487 "reti"
488 :: )
bez tego ryzyko przepełnienia stosu
Rozkład jazdy
1. Wstęp do tematu i motywacja
2. Czym jest zadanie?
3. Przełączanie kontekstu
4. Do czego służy scheduler?
5. Sekcje krytyczne systemu
6. Blokowanie i wznawianie zadań
7. Wywłaszczanie zadań
8. Mutex i inwersja priorytetów
Czy są pytania?
Mutex to taki binarny semafor ?
Jaka jest między nimi różnica ?
Co zdarzyło się na marsie ?
Zadanie nisko priorytetowe L
Zadanie wysoko priorytetowe H
Zadanie średnio priorytetowe M
Czas procesora
Przerwanie wybudzające
A B C D E F
http://research.microsoft.com/en-us/um/people/mbj/Mars_Pathfinder/Authoritative_Account.html
- mutex to więcej niż binarny semafor
- mutex przeciwdziała inwersji priorytetów poprzez dziedziczenie priorytetów
- mutex posiada odwołanie do aktualnego właściciela
- tylko właściciel może odblokować mutex ***
-
Definicja książkowa - czy to wystarczy ?
“Process scheduling algorithm increases the priority of a process (A) to the maximum priority of any other
process waiting for any resource on which A has a resource lock”
Z1 i Z2 reprezentują dwa mutexy chroniące dwa współdzielone zasoby
Opis ponad linją aktywacji wątku reprezentuje blokowany zasób.
Zadanie nisko priorytetowe L
Zadanie wysoko priorytetowe H
Zadanie średnio priorytetowe M2
Czas procesora
Przerwanie wybudzające
A B C D E F G H I
Zadanie średnio priorytetowe M1
Z2
Z1, (Z2)
(Z1)
Problem ten występuje
również w przypadku
mieszania typów
blokad, np drivery IO
(brak propoagacji)
Podbicie priorytetu właściciela i jego zależności
66 static void os_mtx_lock_prio_boost(os_mtx_t *mtx)
67 {
68 os_task_t *task = mtx->owner;
69 const uint_fast8_t task_current_prio = task_current->prio_current;
70
72 if (task->prio_current < task_current_prio) {
74 while (1) {
76 uint_fast8_t prio_new = os_max(task_current->prio_current, task_current_prio);
80 os_taskqueue_reprio(task, prio_new);
81
84 if ((TASKSTATE_WAIT != task->state) ||
85 (OS_TASKBLOCK_MTX != task->block_type)) {
87 break;
88 }
91 task = os_container_of(
92 task->task_queue, os_mtx_t, task_queue)->owner;
93 }
94 }
95 }
Uwaga końcowa
Dziedziczenie priorytetów może pomóc w przypadkach kiedy współdzielenie
danych pomiędzy zadaniami z różnymi poziomami priorytetów jest trudna do
uniknięcia lub wprowadzona została przypadkowo. Nie należy jednak z góry
zakładać że system operacyjny rozwiąże problemy słabo zaprojektowanej
aplikacji.
Copyright (c) 2015 Semihalf. Confidential proprietary
Dziękuje za uwagę
Pytania ?

More Related Content

Similar to Jak napisać własny RTOS!

„Need for speed, czyli jak wycisnąć siódme poty z bazy PostgreSQL” - Wojciech...
„Need for speed, czyli jak wycisnąć siódme poty z bazy PostgreSQL” - Wojciech...„Need for speed, czyli jak wycisnąć siódme poty z bazy PostgreSQL” - Wojciech...
„Need for speed, czyli jak wycisnąć siódme poty z bazy PostgreSQL” - Wojciech...
krakspot
 

Similar to Jak napisać własny RTOS! (14)

100 M pakietów na sekundę dla każdego.
100 M pakietów na sekundę dla każdego. 100 M pakietów na sekundę dla każdego.
100 M pakietów na sekundę dla każdego.
 
ETW w służbie programisty .NET
ETW w służbie programisty .NETETW w służbie programisty .NET
ETW w służbie programisty .NET
 
PLNOG 18 - Robert Ślaski - Programowanie a nie konfiguracja - porozmawiajmy z...
PLNOG 18 - Robert Ślaski - Programowanie a nie konfiguracja - porozmawiajmy z...PLNOG 18 - Robert Ślaski - Programowanie a nie konfiguracja - porozmawiajmy z...
PLNOG 18 - Robert Ślaski - Programowanie a nie konfiguracja - porozmawiajmy z...
 
Programuj wbrew regułom - Bug Legends Quiz Show. Semihalf Barcamp 25/04/2018
Programuj wbrew regułom - Bug Legends Quiz Show. Semihalf Barcamp 25/04/2018Programuj wbrew regułom - Bug Legends Quiz Show. Semihalf Barcamp 25/04/2018
Programuj wbrew regułom - Bug Legends Quiz Show. Semihalf Barcamp 25/04/2018
 
Interfejs RS232C
Interfejs RS232CInterfejs RS232C
Interfejs RS232C
 
Masz wiadomość! Komunikacja wieloprocesorowa w praktyce.
Masz wiadomość! Komunikacja wieloprocesorowa w praktyce.Masz wiadomość! Komunikacja wieloprocesorowa w praktyce.
Masz wiadomość! Komunikacja wieloprocesorowa w praktyce.
 
Efekt motyla w kodzie maszynowym.
Efekt motyla w kodzie maszynowym.Efekt motyla w kodzie maszynowym.
Efekt motyla w kodzie maszynowym.
 
„Need for speed, czyli jak wycisnąć siódme poty z bazy PostgreSQL” - Wojciech...
„Need for speed, czyli jak wycisnąć siódme poty z bazy PostgreSQL” - Wojciech...„Need for speed, czyli jak wycisnąć siódme poty z bazy PostgreSQL” - Wojciech...
„Need for speed, czyli jak wycisnąć siódme poty z bazy PostgreSQL” - Wojciech...
 
Monitoring systemu. Dlaczego mój kardiolog jest bogatym człowiekiem?
Monitoring systemu. Dlaczego mój kardiolog jest bogatym człowiekiem?Monitoring systemu. Dlaczego mój kardiolog jest bogatym człowiekiem?
Monitoring systemu. Dlaczego mój kardiolog jest bogatym człowiekiem?
 
ETW w służbie programisty .NET
ETW w służbie programisty .NETETW w służbie programisty .NET
ETW w służbie programisty .NET
 
Confitura 2018 - Sekretne życie jobów Sparkowych
Confitura 2018 - Sekretne życie jobów SparkowychConfitura 2018 - Sekretne życie jobów Sparkowych
Confitura 2018 - Sekretne życie jobów Sparkowych
 
ZalacznikA
ZalacznikAZalacznikA
ZalacznikA
 
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016
 
DTrace, czyli jak zobaczyć to czego nie widać.
DTrace, czyli jak zobaczyć to czego nie widać.DTrace, czyli jak zobaczyć to czego nie widać.
DTrace, czyli jak zobaczyć to czego nie widać.
 

More from Semihalf

More from Semihalf (20)

Meetup #1 - Świat Komputera Przed Systemem Operacyjnym
Meetup #1 - Świat Komputera Przed Systemem Operacyjnym Meetup #1 - Świat Komputera Przed Systemem Operacyjnym
Meetup #1 - Świat Komputera Przed Systemem Operacyjnym
 
ARM CoreSight - sprawdź, co tak naprawdę robi Twój SoC.
ARM CoreSight - sprawdź, co tak naprawdę robi Twój SoC.ARM CoreSight - sprawdź, co tak naprawdę robi Twój SoC.
ARM CoreSight - sprawdź, co tak naprawdę robi Twój SoC.
 
Embedded Debugging, czyli co kryje się w jądrze?
Embedded Debugging, czyli co kryje się w jądrze?Embedded Debugging, czyli co kryje się w jądrze?
Embedded Debugging, czyli co kryje się w jądrze?
 
Uwaga na buga! GDB w służbie programisty. Barcamp Semihalf S09:E01
Uwaga na buga! GDB w służbie programisty.  Barcamp Semihalf S09:E01Uwaga na buga! GDB w służbie programisty.  Barcamp Semihalf S09:E01
Uwaga na buga! GDB w służbie programisty. Barcamp Semihalf S09:E01
 
Oczyszczacz powietrza i stos sieciowy? Czas na test! Semihalf Barcamp 13/06/2018
Oczyszczacz powietrza i stos sieciowy? Czas na test! Semihalf Barcamp 13/06/2018Oczyszczacz powietrza i stos sieciowy? Czas na test! Semihalf Barcamp 13/06/2018
Oczyszczacz powietrza i stos sieciowy? Czas na test! Semihalf Barcamp 13/06/2018
 
Programuj wbrew regułom. Barcamp Semihalf S08:E02 29/05/2018
Programuj wbrew regułom. Barcamp Semihalf S08:E02 29/05/2018Programuj wbrew regułom. Barcamp Semihalf S08:E02 29/05/2018
Programuj wbrew regułom. Barcamp Semihalf S08:E02 29/05/2018
 
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03
 
Skazani na firmware. ARM Trusted Firmware. S07E02
Skazani na firmware. ARM Trusted Firmware. S07E02Skazani na firmware. ARM Trusted Firmware. S07E02
Skazani na firmware. ARM Trusted Firmware. S07E02
 
Skazani na firmware. Świat komputera przed systemem operacyjnym.
Skazani na firmware. Świat komputera przed systemem operacyjnym.Skazani na firmware. Świat komputera przed systemem operacyjnym.
Skazani na firmware. Świat komputera przed systemem operacyjnym.
 
Złam zasady i stwórz wydajny stos IP przy użyciu DPDK
Złam zasady i stwórz wydajny stos IP przy użyciu DPDKZłam zasady i stwórz wydajny stos IP przy użyciu DPDK
Złam zasady i stwórz wydajny stos IP przy użyciu DPDK
 
Wirtualizacja urządzeń PCI (SR-IOV).
Wirtualizacja urządzeń PCI (SR-IOV).Wirtualizacja urządzeń PCI (SR-IOV).
Wirtualizacja urządzeń PCI (SR-IOV).
 
Software Defined Networks (SDN) na przykładzie rozwiązania OpenContrail.
Software Defined Networks (SDN) na przykładzie rozwiązania OpenContrail.Software Defined Networks (SDN) na przykładzie rozwiązania OpenContrail.
Software Defined Networks (SDN) na przykładzie rozwiązania OpenContrail.
 
Jak stworzyć wysokowydajny i skalowalny stos sieciowy dla 72 rdzeni CPU?
Jak stworzyć wysokowydajny i skalowalny stos sieciowy dla 72 rdzeni CPU?Jak stworzyć wysokowydajny i skalowalny stos sieciowy dla 72 rdzeni CPU?
Jak stworzyć wysokowydajny i skalowalny stos sieciowy dla 72 rdzeni CPU?
 
Hierarchia pamięci w systemach komputerowych.
Hierarchia pamięci w systemach komputerowych.Hierarchia pamięci w systemach komputerowych.
Hierarchia pamięci w systemach komputerowych.
 
Programowanie sterowników w Linuksie.
Programowanie sterowników w Linuksie.Programowanie sterowników w Linuksie.
Programowanie sterowników w Linuksie.
 
Architektura mikrokontrolera pisana słowem.
Architektura mikrokontrolera pisana słowem.Architektura mikrokontrolera pisana słowem.
Architektura mikrokontrolera pisana słowem.
 
Linux KVM - wsparcie dla wirtualizacji w kontekście serwerów ARM.
Linux KVM - wsparcie dla wirtualizacji w kontekście serwerów ARM.Linux KVM - wsparcie dla wirtualizacji w kontekście serwerów ARM.
Linux KVM - wsparcie dla wirtualizacji w kontekście serwerów ARM.
 
SmartNIC - wprowadzenie do inteligentnych interfejsów sieciowych.
SmartNIC - wprowadzenie do inteligentnych interfejsów sieciowych.SmartNIC - wprowadzenie do inteligentnych interfejsów sieciowych.
SmartNIC - wprowadzenie do inteligentnych interfejsów sieciowych.
 
Stosy sieciowe w przestrzeni użytkownika.
Stosy sieciowe w przestrzeni użytkownika.Stosy sieciowe w przestrzeni użytkownika.
Stosy sieciowe w przestrzeni użytkownika.
 
Wirtualizacja sieci na przykładzie OpenContrail vRouter.
Wirtualizacja sieci na przykładzie OpenContrail vRouter.Wirtualizacja sieci na przykładzie OpenContrail vRouter.
Wirtualizacja sieci na przykładzie OpenContrail vRouter.
 

Jak napisać własny RTOS!

  • 1. #3 - BarCamp Semihalf System wbudowany? - Zrób to sam! Jak napisać własny RTOS? Radosław Biernacki radoslaw.biernacki@gmail.com
  • 2. Rozkład jazdy 1. Wstęp do tematu i motywacja 2. Czym jest zadanie? 3. Przełączanie kontekstu 4. Do czego służy scheduler? 5. Sekcje krytyczne systemu 6. Blokowanie i wznawianie zadań 7. Wywłaszczanie zadań 8. Mutex i inwersja priorytetów Chętnie wyjaśnię wątpliwości, proszę o zgłaszanie się w przypadku niejasności. Nagrody za aktywność
  • 3. Czym jest RTOS? RTOS - ang. Real Time Operating System RTOS vs inne systemy np Linux, Windows: - nie dzielą czasu CPU proporcjonalnie do priorytetu zadania - zawsze wykonuje najważniejsze zadanie kosztem pozostałych zadań - deterministyczny czas reakcji na zdarzenia - (zazwyczaj) nie obsługują pamięci wirtualnej - zastosowania sterowanie procesami fizycznymi (rakiety, motoryzacja, automatyka)
  • 4. Po co pisać własny RTOS? - bo nie jest to trudne! - bo to dobre ćwiczenie z zakresu systemów operacyjnych - sucha teoria vs praktyka - git clone https://github.com/rosly/rados.git
  • 5. Ale od czego zacząć? Czym jest zadanie? Zadanie jest programem, który używa procesora w taki sposób, jak gdyby posiadało go na wyłączność. Iluzja równoległego wykonania = okresowe „przełączenia” procesora pomiędzy zadaniami. cpi r19, 0x9A ; 154 and r3, r1 subi r20, 0x3F ; 63 sbci r21, 0x1F ; 31 subi r21, 0x33 ; 51 subi r20, 0x25 ; 37 cpi r21, 0xA4 ; 164 cpi r18, 0x10 ; 16 cpi r18, 0xD0 ; 208 and r3, r13 ori r22, 0x9C ; 156 andi r23, 0x43 ; 67 ori r21, 0x9F ; 159 subi r23, 0xF3 ; 243 ori r22, 0xD5 ; 213 andi r23, 0x40 ; 64 ldd r18, Y+7 ; 0x07 ldd r19, Y+8 ; 0x08 add r24, r18 adc r25, r19 std Y+2, r25 ; 0x02 std Y+1, r24 ; 0x01 ldd r24, Y+1 ; 0x01 ldd r25, Y+2 ; 0x02 call 0xfc2 ; 0xfc2 std Y+4, r25 ; 0x04 std Y+3, r24 ; 0x03 ldd r24, Y+3 ; 0x03 ldd r25, Y+4 ; 0x04 sbiw r24, 0x02 ; 2 std Y+6, r25 ; 0x06 mov r18, r24 ldd r24, Y+7 ; 0x07 ldd r25, Y+8 ; 0x08 movw r30, r24 std Z+20, r18 ; 0x14 ldd r24, Y+5 ; 0x05 ldd r25, Y+6 ; 0x06 movw r30, r24 std Z+11, r1 ; 0x0b std Z+10, r1 ; 0x0a ldd r24, Y+5 ; 0x05 ldd r25, Y+6 ; 0x06 adiw r28, 0x09 ; 9 in r0, 0x3f ; 63 cli out 0x3e, r29 ; 62 out 0x3f, r0 ; 63 out 0x3d, r28 ; 61
  • 6. TCB - Task Control Blok = struktura zadania 39 typedef enum { 40 TASKSTATE_RUNNING = 0, 42 TASKSTATE_READY, 44 TASKSTATE_WAIT, 47 TASKSTATE_DESTROYED, 50 TASKSTATE_INVALID 52 } os_taskstate_t; 50 os_taskqueue_t ready_queue; 70 os_taskqueue_t task_queue; cpi r19, 0x9A and r3, r1 subi r20, 0x3F sbci r21, 0x1F subi r21, 0x33 subi r20, 0x25 cpi r21, 0xA4 cpi r18, 0x10 cpi r18, 0xD0 and r3, r13 ori r22, 0x9C andi r23, 0x43 ori r21, 0x9F subi r23, 0xF3 ori r22, 0xD5 andi r23, 0x40 45 os_task_t* task_current; 78 typedef struct { ... 81 arch_context_t ctx; 84 list_t list; 96 os_taskstate_t state; ... } os_task_t;
  • 7. Cykl życia zadania RUNNING WAITING READY DESTROYED Przydzielenie procesora Oddanie procesora Oczekiwanie na zdarzenieWybudzenie Terminacja ready_queue task1 task2 task3 task4 sem->task_queue task5 task6 mtx->task_queue task7 *task_current = *task8 * gotowe do uruchomienia ale jeszcze nie wykonywane * aktualnie wykonywane * zadania uśpione / oczekujące na zdarzenie &task_idle * nigdy nie może być uśpione
  • 8. Rozkład jazdy 1. Wstęp do tematu i motywacja 2. Czym jest zadanie? 3. Przełączanie kontekstu 4. Do czego służy scheduler? 5. Sekcje krytyczne systemu 6. Blokowanie i wznawianie zadań 7. Wywłaszczanie zadań 8. Mutex i inwersja priorytetów Czy są pytania?
  • 9. 0x41 0x56 0xFF 0x12 0xAB Czym jest kontekst zadania? R0 R30 [ZL] R31 [ZH] R28 [YH] R29 [YH] R26 [XH] R27 [XH] R25 R1 SREG PC SPH SPL cpi r19, 0x9A ; 154 and r3, r1 subi r20, 0x3F ; 63 sbci r21, 0x1F ; 31 subi r21, 0x33 ; 51 subi r20, 0x25 ; 37 cpi r21, 0xA4 ; 164 cpi r18, 0x10 ; 16 cpi r18, 0xD0 ; 208 and r3, r13 ori r22, 0x9C ; 156 andi r23, 0x43 ; 67 ori r21, 0x9F ; 159 subi r23, 0xF3 ; 243 ori r22, 0xD5 ; 213 andi r23, 0x40 ; 64 Rejestry RAM ROM Co zrobić żeby zapisać to -> Użyć innego stosu niż <- ten I skoczyć poza to <- to
  • 10. Zapisanie kontekstu do TCB 290 #define arch_contextstore_i() 291 __asm__ __volatile__ ( 293 "push r16" 298 "in r16, __SREG__" 299 "sbr r16, 0x80" 300 "push r16" 306 "push r28" 307 "push r29" 310 "push r0" 311 "push r1" … (z pominięciem r16, r28 i r29) 339 "push r31" 346 "in r28, __SP_L__" 347 "in r29, __SP_H__" 353 "lds r30, task_current" 354 "lds r31, task_current+1" 355 "st Z, r28" 356 "std Z+1, r29" R0 - R31 PC SP TCB starego zadania ret PC R16 SREG R28 R29 R0 - R15 R17 - R27 R30 - R31 SREG task_current SP TCB nowego zadania SP Stos starego zadania 45 os_task_t* task_current; 78 typedef struct { 81 arch_context_t ctx; 84 list_t list; 96 os_taskstate_t state; ... } os_task_t; 56 typedef struct { 57 uint16_t sp; 58 } arch_context_t;
  • 11. Przywrócenie kontekstu z TCB 391 #define arch_contextrestore_i() 392 __asm__ __volatile__ ( 402 "lds r30, task_current" 403 "lds r31, task_current+1" 404 "ld r16, Z" 405 "ldd r17, Z+1" 406 "out __SP_L__, r16" 407 "out __SP_H__, r17" 410 "pop r31" … (z pominięciem r16, r28 i r29) 438 "pop r1" 439 "pop r0" 440 "pop r29" 441 "pop r28" 447 "pop r16" 455 "out __SREG__, r16" 456 "pop r16" 459 "ret" R0 - R31 PC SP TCB starego zadania ret PC R16 SREG R28 R29 R0 - R15 R17 - R27 R30 - R31 SREG task_current SP TCB nowego zadania ret PC R16 SREG R28 R29 R0 - R15 R17 - R27 R30 - R31 SP * skok do wcześniej przerwanego kodu * podmiana wskaźnika zadania Stos starego zadania Stos nowego zadania całkiem nowy 45 os_task_t* task_current; 78 typedef struct { 81 arch_context_t ctx; 84 list_t list; 96 os_taskstate_t state; ... } os_task_t; 56 typedef struct { 57 uint16_t sp; 58 } arch_context_t;
  • 12. Jak inicjalizowany jest stos przy tworzeniu zadania? 204 void arch_task_init(os_task_t * task, void* stack_param, 205 size_t stack_size, os_taskproc_t proc, 206 void* param) 207 { 208 uint8_t *stack = ((uint8_t*)stack_param) + stack_size - 1; 209 211 *(stack--) = (uint8_t)((uint16_t)arch_task_start & 0xFF); 212 *(stack--) = (uint8_t)((uint16_t)arch_task_start >> 8);; 213 *(stack--) = 0; /* R16 */ 214 *(stack--) = 1 << SREG_I; ... 252 *(stack--) = 0; /* R31 */ 253 255 task->ctx.sp = (uint16_t)stack; 256 } ret PC R16 SREG R28 R29 R0 - R15 R17 - R27 R30 - R31 Stos zadania 78 typedef struct { 81 arch_context_t ctx; 84 list_t list; 96 os_taskstate_t state; ... } os_task_t; 56 typedef struct { 57 uint16_t sp; 58 } arch_context_t;
  • 13. Rozkład jazdy 1. Wstęp do tematu i motywacja 2. Czym jest zadanie? 3. Przełączanie kontekstu 4. Do czego służy scheduler? 5. Sekcje krytyczne systemu 6. Blokowanie i wznawianie zadań 7. Wywłaszczanie zadań 8. Mutex i inwersja priorytetów Czy są pytania?
  • 14. Scheduler i priorytetyzacja zadań Task1 hi prio Task2 Task3 Task4 low prio O(1) Enqueue any O(1) Dequeue top prio with FIFO for equal prio os_taskqueue_t ready_queue : READY -> RUNNING 254 os_task_t *task = os_taskqueue_dequeue(&ready_queue) *** zły projekt = głodzenie „fair schedule”
  • 15. Enqueue ze złożonością O(1) 181 static inline void os_task_makeready(os_task_t *task) 182 { 183 task->state = TASKSTATE_READY; 184 os_taskqueue_enqueue(&ready_queue, task); 185 } 269 void OS_HOT os_taskqueue_enqueue( 270 os_taskqueue_t* task_queue, 271 os_task_t* task) 272 { 274 list_append(&(task_queue->tasks[task->prio_current]), &(task->list)); 276 278 arch_bitmask_set(task_queue->mask, task->prio_current); 279 } 154 typedef struct os_taskqueue_tag { 157 list_t tasks[OS_CONFIG_PRIOCNT]; 160 arch_bitmask_t mask; 162 } os_taskqueue_t; 1 0 1 1 Head list_t list_t Head list_t Head list_t list_t Head arch_bitmask_t
  • 16. Dequeue ze złożonością O(1) 359 os_task_t* OS_HOT os_taskqueue_dequeue( os_taskqueue_t* task_queue) 360 { 361 uint_fast8_t maxprio; 362 364 maxprio = arch_bitmask_fls(task_queue->mask); 365 if (0 == maxprio) 366 { 367 return NULL; 368 } 369 --maxprio; /* convert to index counted from 0 */ 370 371 return os_taskqueue_intdequeue(task_queue, maxprio); 372 } 334 static os_task_t* os_taskqueue_intdequeue( 335 os_taskqueue_t *task_queue, 336 uint_fast8_t maxprio) 337 { 338 list_t *task_list; 339 os_task_t *task; 340 342 task_list = &task_queue->tasks[maxprio]; 343 task = os_container_of(list_detachfirst(task_list), os_task_t, list); 344 if (list_is_empty(task_list)) 345 { 347 arch_bitmask_clear(task_queue->mask, maxprio); 348 } 349 351 return task; 352 }
  • 17. Dequeue ze złożonością O(1) 154 typedef struct os_taskqueue_tag { 157 list_t tasks[OS_CONFIG_PRIOCNT]; 160 arch_bitmask_t mask; 162 } os_taskqueue_t; 1 0 1 1 Head list_t list_t Head list_t Head list_t list_t Head arch_bitmask_t 131 static inline uint_fast8_t arch_bitmask_fls(arch_bitmask_t bitfield) 132 { 133 return ((bitfield == 0)? 134 0 : ((sizeof(unsigned int) * 8) - __builtin_clz((unsigned int)bitfield))); 135 } 271 uint_fast8_t arch_bitmask_fls(arch_bitmask_t bitfield) 272 { 273 static const OS_PROGMEM uint8_t log2lkup[256] = { 274 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U... 275 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U... ... 289 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U... 290 }; 291 292 return log2lkup[bitfield]; 293 }
  • 18. Blokowanie schedulera vs sekcje krytyczne 135 arch_criticalstate_t cristate; 136 138 arch_critical_enter(cristate); ... 144 arch_critical_exit(cristate); - użytkownik może wyłączyć przerwania na czas wywołań OS - kod przerwań może obsługiwać zagnieżdżanie (włączenie przerwań przed powrotem) 62 volatile os_atomic_t isr_nesting = 0; 137 extern volatile os_atomic_t sched_lock; ... 219 static inline void os_scheduler_intlock(void) 220 { 221 os_atomic_inc(sched_lock); 222 } 223 224 static inline void os_scheduler_intunlock(bool sync) 225 { 226 os_atomic_dec(sched_lock); 227 228 if (!sync) 229 { 232 os_schedule(1); 233 } 234 } 0 - kontekst zadania 1 - kontekst przerwania >1 - zagnieżdżone przerwanie
  • 19. Rozkład jazdy 1. Wstęp do tematu i motywacja 2. Czym jest zadanie? 3. Przełączanie kontekstu 4. Do czego służy scheduler? 5. Sekcje krytyczne systemu 6. Blokowanie i wznawianie zadań 7. Wywłaszczanie zadań 8. Mutex i inwersja priorytetów Czy są pytania?
  • 20. Semafor - przypomnienie 83 void os_sem_create(os_sem_t* sem, os_atomic_t init_value); 109 void os_sem_destroy(os_sem_t* sem); 140 os_retcode_t OS_WARN_UNUSEDRET os_sem_down( 141 os_sem_t* sem, 142 uint_fast16_t timeout_ticks); 165 void os_sem_up_sync(os_sem_t* sem, bool sync); 54 typedef volatile arch_atomic_t os_atomic_t; 64 typedef struct os_sem_tag { 66 os_taskqueue_t task_queue; 69 os_atomic_t value; 71 } os_sem_t; sem->value > 0 os_sem_up() nieblokujące dowolne zadanie os_sem_down() uśpienie wybudzenie sem->task_queue
  • 21. This is your last chance. After this, there is no turning back Embedded ? Everything runs in cloud now ... How deep the rabbit hole goes ? (4 strony listingów)
  • 22. Kod os_sem_down() 78 os_retcode_t OS_WARN_UNUSEDRET os_sem_down( 79 os_sem_t* sem, 80 uint_fast16_t timeout_ticks) 81 { 82 os_retcode_t ret; 83 os_timer_t timer; 84 arch_criticalstate_t cristate; 85 86 OS_ASSERT(0 == isr_nesting); /* przerwanie ? */ 87 OS_ASSERT(task_current == &task_idle); 89 92 arch_critical_enter(cristate); /* sekcja krytyczna */ 93 do 94 { 95 if (sem->value > 0) /* potencjalna optymalizacja dla ARM */ 96 { 102 --(sem->value); 103 ret = OS_OK; 104 break; /* po kłopocie */ 105 } 108 if (OS_TIMEOUT_TRY == timeout_ticks) 109 { 111 ret = OS_WOULDBLOCK; /* musiał bym uśpić zadanie */ 112 break; 113 } 114 116 if (OS_TIMEOUT_INFINITE != timeout_ticks) /* pominiemy detale */ 117 { 119 os_blocktimer_create(&timer, os_sem_timerclbck, timeout_ticks); 120 } 121 123 os_task_block_switch(&(sem->task_queue), OS_TASKBLOCK_SEM); 124 127 os_blocktimer_destroy(task_current); 128 131 ret = task_current->block_code; 132 133 } while (0); 134 arch_critical_exit(cristate); 135 136 return ret; 137 } powrót dopiero po wybudzeniu dalej na następnym slajdzie
  • 23. Wstrzymywanie zadania 515 void OS_HOT os_task_block_switch( 516 os_taskqueue_t* task_queue, 517 os_taskblock_t block_type) 518 { 520 os_task_makewait(task_queue, block_type); 524 arch_context_switch(os_taskqueue_dequeue(&ready_queue)); 531 task_current->state = TASKSTATE_RUNNING; 532 } 187 static inline void os_task_makewait( 188 os_taskqueue_t *task_queue, 189 os_taskblock_t block_type) 190 { 193 task_current->state = TASKSTATE_WAIT; 194 task_current->block_type = block_type; 195 os_taskqueue_enqueue(task_queue, task_current); 196 } Pseudo kod (oryginalnie ASM) 61 void OS_NAKED OS_HOT arch_context_switch( os_task_t * new_task) 62 { 63 arch_contextstore(); /* podobna do już omawianej */ 106 task_current = new_task; /* przemilczana poprzednio */ 107 arch_contextrestore(); /* podobna do już omawianej */ 108 } skok przy powrocie ??? 1 0 1 1 Head list_t list_t Head list_t Head list_t list_t Head arch_bitmask_t gdyby ktoś zapomniał sem->task_queue i ready_queue -> &(sem->task_queue) nigdy =NULL bo idle_task
  • 24. Gdzie jesteśmy ? 83 void os_sem_create(os_sem_t* sem, os_atomic_t init_value); 109 void os_sem_destroy(os_sem_t* sem); 140 os_retcode_t OS_WARN_UNUSEDRET os_sem_down( 141 os_sem_t* sem, 142 uint_fast16_t timeout_ticks); 165 void os_sem_up_sync(os_sem_t* sem, bool sync); 54 typedef volatile arch_atomic_t os_atomic_t; 64 typedef struct os_sem_tag { 66 os_taskqueue_t task_queue; 69 os_atomic_t value; 71 } os_sem_t; value > 0 os_sem_up() nieblokujące dowolne zadanie os_sem_down() uśpienie wybudzenie sem->task_queue
  • 25. Kod os_sem_up_sync() 147 void os_sem_up_sync(os_sem_t* sem, bool sync) 148 { 149 arch_criticalstate_t cristate; 150 os_task_t *task; 151 159 OS_ASSERT((isr_nesting == 0) || (sync == false)); 161 162 arch_critical_enter(cristate); 163 165 OS_ASSERT(sem->value < (OS_ATOMIC_MAX - 1)); 166 168 task = os_taskqueue_dequeue(&(sem->task_queue)); 169 if (NULL == task) /* czy czeka jakies zadanie ? */ 170 { 172 ++(sem->value); /* po kłopocie */ 173 } else { 177 os_blocktimer_destroy(task); 178 179 task->block_code = OS_OK; /* powód wybudzenia */ 180 os_task_makeready(task); /* tylko wybudzenie */ 181 186 if (!sync) /* czy zapobiegać context switch */ 187 { 190 os_schedule(1); /* potencjalne przydzielenie CPU */ 191 } 192 } 193 arch_critical_exit(cristate); 194 } 181 static inline void os_task_makeready(os_task_t *task) 182 { 183 task->state = TASKSTATE_READY; 184 os_taskqueue_enqueue(&ready_queue, task); 185 }
  • 26. Wznawianie zadań 467 void OS_HOT os_schedule(uint_fast8_t higher_prio) 468 { 469 os_task_t *new_task; 470 474 if (OS_LIKELY((isr_nesting <= 1) && (0 == sched_lock))) 475 { 478 new_task = os_taskqueue_dequeue_prio( 479 &ready_queue, task_current->prio_current + higher_prio); 480 482 if (NULL != new_task) /* może NULL, patrz higher_prio */ 483 { 486 os_task_makeready(task_current); /* bieżące musi przejść do READY */ 488 if (0 == isr_nesting) /* czy przerwanie ? */ 489 { 490 arch_context_switch(new_task); 491 } else { 495 task_current = new_task; /* przerwanie!!… context switch na końcu ISR */ 496 } 497 /* tu wrócimy dopiero po ponownym przydzieleniu CPU */ 498 task_current->state = TASKSTATE_RUNNING; 499 } 500 } 501 } 181 static inline void os_task_makeready(os_task_t *task) 182 { 183 task->state = TASKSTATE_READY; 184 os_taskqueue_enqueue(&ready_queue, task); 185 } Pseudo kod (oryginalnie ASM) 61 void OS_NAKED OS_HOT arch_context_switch( os_task_t * new_task) 62 { 63 arch_contextstore(); 106 task_current = new_task; 107 arch_contextrestore(); 108 } skok przy ret ???
  • 27. Rozkład jazdy 1. Wstęp do tematu i motywacja 2. Czym jest zadanie? 3. Przełączanie kontekstu 4. Do czego służy scheduler? 5. Sekcje krytyczne systemu 6. Blokowanie i wznawianie zadań 7. Wywłaszczanie zadań 8. Mutex i inwersja priorytetów Czy są pytania?
  • 28. Preemption - Wywłaszczanie zadań Zadanie nisko priorytetowe Przerwanie wybudzające zadanie wysoko priorytetowe Zadanie wysoko priorytetowe Czas procesora Przydział procesora czas reakcji na zdarzenie sekcja krytyczna kernela zgłoszenie przerwania Zadanie nisko priorytetowe Przerwanie wybudzające zadanie wysoko priorytetowe Zadanie wysoko priorytetowe Czas procesora Przydział procesora czas reakcji na zdarzenie sekcja krytyczna kernela zgłoszenie przerwania Cooperative multitasking np Contiki Preemptive multitasking omawiany system
  • 29. Przykład przerwania przez takt zegara systemowego 89 #define OS_ISR __attribute__((naked, signal, used, externally_visible)) 183 void OS_ISR TIMER1_COMPA_vect(void) 184 { 185 arch_contextstore_i(tick); 186 189 os_tick(); /* potencjalne przełączenie task_current w os_sched() */ 194 195 arch_contextrestore_i(tick); /* potencjalny context switch i skok przez reti*/ 196 }
  • 30. Kod przyjęcia i wyjścia z przerwania: 407 #define arch_contextrestore_i(_isrName) 408 __asm__ __volatile__ ( 410 "cli" /* zagnieżdżone przerwania */ 412 "lds r16, isr_nesting" 413 "dec r16" 414 "sts isr_nesting, r16" 416 "brne isr_contextrestore_nested_%=" 418 "lds r30, task_current" 419 "lds r31, task_current+1" 420 "ld r16, Z" 421 "ldd r17, Z+1" 422 "out __SP_L__, r16" 423 "out __SP_H__, r17" 306 #define arch_contextstore_i(_isrName) 307 __asm__ __volatile__ ( 309 "push r16" ... 355 "push r31" 357 "lds r16, isr_nesting" 358 "inc r16" 359 "sts isr_nesting, r16" 362 "in r28, __SP_L__" 363 "in r29, __SP_H__" 364 "eor r1, r1" 366 "cpi r16, 1" 367 "brne isr_contextstore_nested_%=" 369 "lds r30, task_current" /* tylko jeśli nesting == 1 */ 370 "lds r31, task_current+1" 371 "st Z, r28" 372 "std Z+1, r29" 373 "isr_contextstore_nested_%=:" 374 :: ) 424 "isr_contextrestore_nested_%=:" 426 "pop r31" ... 457 "pop r28" 463 "pop r16" 469 "sbrc r16, 7" /* czy jest IE ? */ 470 "rjmp isr_contextrestore_enableint_%=" 471 "out __SREG__, r16" /* tu nie ma IE */ 472 "pop r16" 475 "ret" 476 "isr_contextrestore_enableint_%=:" 481 "cbr r16, 0x80" /* jeszcze nie teraz */ 482 "out __SREG__, r16" /* jedna instrukcja */ 483 "pop r16" 487 "reti" 488 :: ) bez tego ryzyko przepełnienia stosu
  • 31. Rozkład jazdy 1. Wstęp do tematu i motywacja 2. Czym jest zadanie? 3. Przełączanie kontekstu 4. Do czego służy scheduler? 5. Sekcje krytyczne systemu 6. Blokowanie i wznawianie zadań 7. Wywłaszczanie zadań 8. Mutex i inwersja priorytetów Czy są pytania?
  • 32. Mutex to taki binarny semafor ? Jaka jest między nimi różnica ?
  • 33. Co zdarzyło się na marsie ? Zadanie nisko priorytetowe L Zadanie wysoko priorytetowe H Zadanie średnio priorytetowe M Czas procesora Przerwanie wybudzające A B C D E F http://research.microsoft.com/en-us/um/people/mbj/Mars_Pathfinder/Authoritative_Account.html - mutex to więcej niż binarny semafor - mutex przeciwdziała inwersji priorytetów poprzez dziedziczenie priorytetów - mutex posiada odwołanie do aktualnego właściciela - tylko właściciel może odblokować mutex *** -
  • 34. Definicja książkowa - czy to wystarczy ? “Process scheduling algorithm increases the priority of a process (A) to the maximum priority of any other process waiting for any resource on which A has a resource lock” Z1 i Z2 reprezentują dwa mutexy chroniące dwa współdzielone zasoby Opis ponad linją aktywacji wątku reprezentuje blokowany zasób. Zadanie nisko priorytetowe L Zadanie wysoko priorytetowe H Zadanie średnio priorytetowe M2 Czas procesora Przerwanie wybudzające A B C D E F G H I Zadanie średnio priorytetowe M1 Z2 Z1, (Z2) (Z1) Problem ten występuje również w przypadku mieszania typów blokad, np drivery IO (brak propoagacji)
  • 35. Podbicie priorytetu właściciela i jego zależności 66 static void os_mtx_lock_prio_boost(os_mtx_t *mtx) 67 { 68 os_task_t *task = mtx->owner; 69 const uint_fast8_t task_current_prio = task_current->prio_current; 70 72 if (task->prio_current < task_current_prio) { 74 while (1) { 76 uint_fast8_t prio_new = os_max(task_current->prio_current, task_current_prio); 80 os_taskqueue_reprio(task, prio_new); 81 84 if ((TASKSTATE_WAIT != task->state) || 85 (OS_TASKBLOCK_MTX != task->block_type)) { 87 break; 88 } 91 task = os_container_of( 92 task->task_queue, os_mtx_t, task_queue)->owner; 93 } 94 } 95 }
  • 36. Uwaga końcowa Dziedziczenie priorytetów może pomóc w przypadkach kiedy współdzielenie danych pomiędzy zadaniami z różnymi poziomami priorytetów jest trudna do uniknięcia lub wprowadzona została przypadkowo. Nie należy jednak z góry zakładać że system operacyjny rozwiąże problemy słabo zaprojektowanej aplikacji.
  • 37. Copyright (c) 2015 Semihalf. Confidential proprietary Dziękuje za uwagę Pytania ?