#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? 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 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.
TCB - Task Control Blok = struktura zadania 45 os_task_t* task_current; cpi and subi sbci subi subi cpi cpi cpi and ori andi ori subi ori andi r19, 0x9A r3, r1 r20, 0x3F r21, 0x1F r21, 0x33 r20, 0x25 r21, 0xA4 r18, 0x10 r18, 0xD0 r3, r13 r22, 0x9C r23, 0x43 r21, 0x9F r23, 0xF3 r22, 0xD5 r23, 0x40 78 typedef struct {... 81 arch_context_t ctx; 84 list_t list; 96 os_taskstate_t state;... } os_task_t; 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;
Cykl życia zadania ready_queue task1 task2 task3 task4 * gotowe do uruchomienia ale jeszcze nie wykonywane READY Wybudzenie Przydzielenie procesora Oddanie procesora Oczekiwanie na zdarzenie * aktualnie wykonywane RUNNING Terminacja *task_current = *task8 WAITING DESTROYED &task_idle * nigdy nie może być uśpione sem->task_queue task5 task6 * zadania uśpione / oczekujące na zdarzenie mtx->task_queue task7
Rozkład jazdy Czy są pytania? 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
Czym jest kontekst zadania? ROM Co zrobić żeby zapisać to -> R0 R1 R25 R26 [XH] Rejestry SREG PC 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 I skoczyć poza to <- to R27 [XH] SPH SPL R28 [YH] R29 [YH] R30 [ZL] R31 [ZH] 0x41 0x56 0xFF 0x12 0xAB Użyć innego stosu niż <- ten RAM
Zapisanie kontekstu do TCB SP TCB starego zadania Stos starego zadania ret PC R16 SREG R28 R29 R0 - R15 R17 - R27 R30 - R31 task_current SREG PC SP R0 - R31 SP TCB nowego zadania 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" 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 SP TCB starego zadania Stos starego zadania ret PC R16 SREG R28 R29 R0 - R15 R17 - R27 R30 - R31 task_current * podmiana wskaźnika zadania SREG PC SP R0 - R31 SP TCB nowego zadania Stos nowego zadania ret PC R16 SREG R28 R29 R0 - R15 R17 - R27 R30 - R31 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" 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; * skok do wcześniej przerwanego kodu
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 } Stos zadania ret PC R16 SREG R28 R29 R0 - R15 R17 - R27 R30 - R31 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 Czy są pytania? 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
Scheduler i priorytetyzacja zadań *** fair schedule zły projekt = głodzenie os_taskqueue_t ready_queue : READY -> RUNNING 254 os_task_t *task = os_taskqueue_dequeue(&ready_queue) O(1) Enqueue any Task1 hi prio Task2 Task3 Task4 low prio O(1) Dequeue top prio with FIFO for equal prio
Enqueue 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; 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 } arch_bitmask_t 1 0 1 Head list_t list_t Head Head list_t 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 } 1 Head list_t list_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; 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 } arch_bitmask_t 1 0 1 1 Head list_t list_t Head Head list_t Head list_t list_t 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; 0 - kontekst zadania 1 - kontekst przerwania >1 - zagnieżdżone przerwanie 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 }
Rozkład jazdy Czy są pytania? 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
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); 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; 165 void os_sem_up_sync(os_sem_t* sem, bool sync); os_sem_down() sem->task_queue uśpienie wybudzenie sem->value > 0 os_sem_up() nieblokujące dowolne zadanie
This is your last chance. After this, there is no turning back How deep the rabbit hole goes? (4 strony listingów) Embedded? Everything runs in cloud now...
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 gdyby ktoś zapomniał 515 void OS_HOT os_task_block_switch( sem->task_queue i ready_queue -> 516 os_taskqueue_t* task_queue, 517 os_taskblock_t block_type) 518 { nigdy =NULL bo idle_task 520 os_task_makewait(task_queue, block_type); 524 arch_context_switch(os_taskqueue_dequeue(&ready_queue)); arch_bitmask_t 1 0 1 1 Head list_t list_t Head Head list_t Head list_t list_t 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 } &(sem->task_queue)??? 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
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); 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; 165 void os_sem_up_sync(os_sem_t* sem, bool sync); os_sem_down() sem->task_queue uśpienie wybudzenie value > 0 os_sem_up() nieblokujące dowolne zadanie
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(); skok przy ret 108 }???
Rozkład jazdy Czy są pytania? 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
Preemption - Wywłaszczanie zadań Cooperative multitasking np Contiki Przydział procesora Przerwanie wybudzające zadanie wysoko priorytetowe Zadanie wysoko priorytetowe zgłoszenie przerwania sekcja krytyczna kernela czas reakcji na zdarzenie Zadanie nisko priorytetowe Czas procesora Preemptive multitasking omawiany system Przydział procesora Przerwanie wybudzające zadanie wysoko priorytetowe Zadanie wysoko priorytetowe zgłoszenie przerwania sekcja krytyczna kernela czas reakcji na zdarzenie Zadanie nisko priorytetowe Czas procesora
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) 306 #define arch_contextstore_i(_isrname) 408 asm volatile ( 307 asm volatile ( 410 "cli" /* zagnieżdżone przerwania */ 309 "push r16" 412 "lds r16, isr_nesting"... 413 "dec r16" 355 "push r31" 414 "sts isr_nesting, r16" 357 "lds r16, isr_nesting" 416 "brne isr_contextrestore_nested_%=" 358 "inc r16" 418 "lds r30, task_current" 359 "sts isr_nesting, r16" 419 "lds r31, task_current+1" 362 "in r28, SP_L " 420 "ld r16, Z" 363 "in r29, SP_H " 421 "ldd r17, Z+1" 364 "eor r1, r1" 422 "out SP_L, r16" 366 "cpi r16, 1" 423 "out SP_H, r17" 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 :: ) bez tego ryzyko przepełnienia stosu 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 :: )
Rozkład jazdy Czy są pytania? 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
Mutex to taki binarny semafor? Jaka jest między nimi różnica?
Co zdarzyło się na marsie? A B C D E F Przerwanie wybudzające Zadanie wysoko priorytetowe H Zadanie średnio priorytetowe M Zadanie nisko priorytetowe L http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/authoritative_account.html Czas procesora - 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. A B C D E F G H I Przerwanie wybudzające Problem ten występuje również w przypadku mieszania typów blokad, np drivery IO (brak propoagacji) Zadanie wysoko priorytetowe H Zadanie średnio priorytetowe M1 Zadanie średnio priorytetowe M2 Zadanie nisko priorytetowe L Z2 Z1, (Z2) (Z1) Czas procesora
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.
Dziękuje za uwagę Pytania? Copyright (c) 2015 Semihalf. Confidential proprietary