1 APM Scheduler 分析 2 3 void AP_Scheduler::run(uint32_t time_available) 4 { 5 uint32_t run_started_usec = AP_HAL::micros(); 6 uint32_t now = run_started_usec; 7 8 for (uint8_t i=0; i<_num_tasks; i++) { 9 // dt表明了當前任務已經調度了多少個週期 10 uint16_t dt = _tick_counter - _last_run[i]; 11 12 // interval_ticks表明這個任務須要多少個調度週期才能運行 13 // 若是任務調度週期(_loop_rate_hz)設置成50hz,也就是說20ms調度一次, _tick_counter+1 14 // 若是一個任務被設置成以10hz運行,也就是100ms運行一次,須要進行50hz/10hz=5次調度,此任務才能被運行一次 15 uint16_t interval_ticks = _loop_rate_hz / _tasks[i].rate_hz; 16 17 // 若是調度次數小於1,也就是說任務調度的頻率比_loop_rate_hz還快,則只調度一次 18 // 這裏經過_loop_rate_hz限制了任務調度的最快頻率 19 if (interval_ticks < 1) { 20 interval_ticks = 1; 21 } 22 23 // 若是dt>=interval_ticks,表明當前任務已經達到了所需調度的週期,能夠進行調度了 24 if (dt >= interval_ticks) { 25 // _task_time_allowed表明了當前任務最大容許被運行的時間 26 // 這個時間在這個調度策略中並沒有卵用,純屬雞肋!!! 27 // 很差意思,也並非徹底雞肋,他的做用體如今當調度器剩餘時間是否夠當前任務最大所需時間 28 29 // this task is due to run. Do we have enough time to run it? 30 _task_time_allowed = _tasks[i].max_time_micros; 31 32 // 當前任務所需的調度週期已經超過2次所需時間,表明此任務本應該被執行了,可是被延遲了 33 // 此處沒有作任何處理,只是作了調試輸出 34 // 因此說每一個任務即便你規定了他的調度頻率,只是一個指望值,他受到整個系統調度的影響,他的實時性是不肯定的 35 if (dt >= interval_ticks*2) { 36 // we've slipped a whole run of this task! 37 if (_debug > 4) { 38 ::printf("Scheduler slip task[%u-%s] (%u/%u/%u)\n", 39 (unsigned)i, 40 _tasks[i].name, 41 (unsigned)dt, 42 (unsigned)interval_ticks, 43 (unsigned)_task_time_allowed); 44 } 45 } 46 47 // time_available表明當前調度器還剩餘多少時間來進行任務調度 48 // time_available = 1000000/_loop_rate_hz= 20ms 49 // 若是當前任務單次最大被容許運行的時間比剩餘的時間還小,證實系統還有足夠的時間來運行他,那麼就能夠運行這次任務 50 // 不然跳出,繼續輪訓列表中的下一次任務,看誰還有機會被執行 51 if (_task_time_allowed <= time_available) { 52 // run it 53 //記下當前任務運行的開始時間 54 _task_time_started = now; 55 current_task = i; 56 _tasks[i].function(); 57 current_task = -1; 58 59 // 更新當前任務運行完成後的tick,爲計算下一次dt作準備 60 // record the tick counter when we ran. This drives 61 // when we next run the event 62 _last_run[i] = _tick_counter; 63 64 // work out how long the event actually took 65 now = AP_HAL::micros(); 66 // 計算出當前任務運行的時間 67 uint32_t time_taken = now - _task_time_started; 68 69 // 若是當前任務運行的時間超過了最大容許的時間,表明當前任務已經超時運行 70 // 這裏只是作了打印輸出,並無什麼卵用 71 if (time_taken > _task_time_allowed) { 72 // the event overran! 73 if (_debug > 4) { 74 ::printf("Scheduler overrun task[%u-%s] (%u/%u)\n", 75 (unsigned)i, 76 _tasks[i].name, 77 (unsigned)time_taken, 78 (unsigned)_task_time_allowed); 79 } 80 } 81 // 若是當前任務運行的時間已經超過調度器剩餘的時間,那麼就直接退出任務列表調度 82 // 但前任務後面的任務就不調度了 83 if (time_taken >= time_available) { 84 goto update_spare_ticks; 85 } 86 // 把調度器剩餘可用的時間更新,若是當前任務超時運行,那麼下一個任務就可能執行不到了 87 time_available -= time_taken; 88 } 89 } 90 } 91 92 // update number of spare microseconds 93 _spare_micros += time_available; 94 95 update_spare_ticks: 96 _spare_ticks++; 97 if (_spare_ticks == 32) { 98 _spare_ticks /= 2; 99 _spare_micros /= 2; 100 } 101 }