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 }