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 }