Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Jak napisać własny RTOS!

253 Aufrufe

Veröffentlicht am

Jak realizowana jest wielozadaniowość na pojedynczym CPU oraz jakie są jej konsekwencje? Czy “mutex to taki binarny semafor”? Dlaczego Windows nie jest używany do sterowania rakietami oraz jaki popularny błąd omal nie spowodował utraty jednej z misji NASA?

Veröffentlicht in: Software
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

Jak napisać własny RTOS!

  1. 1. #3 - BarCamp Semihalf System wbudowany? - Zrób to sam! Jak napisać własny RTOS? Radosław Biernacki radoslaw.biernacki@gmail.com
  2. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 32. Mutex to taki binarny semafor ? Jaka jest między nimi różnica ?
  33. 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. 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. 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. 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. 37. Copyright (c) 2015 Semihalf. Confidential proprietary Dziękuje za uwagę Pytania ?

×