前置知识
- C语言基础及其指针
- 数据结构与算法基础
- 汇编基础
- FreeRTOS以下简称OS
trace
前缀开头的是用来调试追溯的
OS结构体解析
- 每个任务创建后都会有一个TCB结构的数据
- TCB(Task Control Block),用于存放任务的参数,包含栈地址、栈顶指针、优先级、状态链表和事件链表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack; // 此参数适中指向任务的(TCB)栈顶,并且必须放到当前结构体(TCB)的第一个位置,因为后面汇编会通过偏移直接访问
xMPU_SETTINGS xMPUSettings; // 存放MPU的相关设置
UBaseType_t uxCoreAffinityMask; // 在多核系统中,用于指定任务可以运行在哪个核心上,通过掩码的方式
ListItem_t xStateListItem; // 任务的状态链表结点,用于将任务挂载到任务列表中,根据不同的优先级挂载到不同的索引下
ListItem_t xEventListItem; // 事件链表结点
UBaseType_t uxPriority; // 任务的优先级,0为最低优先级
StackType_t * pxStack; // 任务栈的地址,始终指向栈底(栈的开始)
volatile BaseType_t xTaskRunState; /**< Used to identify the core the task is running on, if the task is running. Otherwise, identifies the task's state - not running or yielding. */
UBaseType_t uxTaskAttributes; /**< Task's attributes - currently used to identify the idle tasks. */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; // 存放任务的名称,最大长度为16
BaseType_t xPreemptionDisable; // 当OS配置为抢占时,禁止任务被被抢占。
StackType_t * pxEndOfStack; // 指向任务最高合法的地址
UBaseType_t uxCriticalNesting; // 用于记录临界区的嵌套深度,每当进入一个临界区此值递增若退出递减
UBaseType_t uxTCBNumber; // 存放了一个递增的数值,每次任务创建此值自动递增,用于调试器识别任务是否被删除或者重建
UBaseType_t uxTaskNumber; // 存放了一个数字,用于第三方工具调试追踪
UBaseType_t uxBasePriority; // 上一次为该任务分配的优先级,用于互斥锁继承机制
UBaseType_t uxMutexesHeld; // 任务当前持有的互斥锁数量
TaskHookFunction_t pxTaskTag;
void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /**< Stores the amount of time the task has spent in the Running state. */
configTLS_BLOCK_TYPE xTLSBlock; /**< Memory block used as Thread Local Storage (TLS) Block for the task. */
// 每个OS任务都有一个任务通知数组。每个任务都有“挂起”或“非挂起”的通知状态和一个32位的通知值
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; // 任务通知值
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; // 任务通知状态
/* See the comments in FreeRTOS.h with the definition of
* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
uint8_t ucStaticallyAllocated; // 如果任务是通过静态方法创建的则为pdTRUE,确保不会尝试释放当前任务
uint8_t ucDelayAborted; // 标记任务的延迟状态是否被中断(如通过 `xTaskAbortDelay()` 强制唤醒任务)
int iTaskErrno;
} tskTCB;OS的任务创建分析
- TCB(Task Control Block),用于存放任务的参数,包含栈地址、栈顶指针、优先级、状态链表和事件链表
- 在OS中任务创建中有两个函数可以创建OS任务,以下是两个函数的简单介绍
xTaskCreate
:动态分配一个大小为sizeof(TCB)的内存块,包括栈等,存放TCB数据xTaskCreateStatic
:通过用户自己定义TCB内存块,包括栈,并且必须是持续存在的不能在函数栈里声明
xTaskCreate
动态创建任务
- 该函数用于动态创建一个OS任务,以下是主要的参数说明
- pxTaskCode:存放任务函数的地址
- pcName:任务的名称
- uxStackDepth:栈深度,即栈的大小
- pvParameters:任务参数
- uxPriority:任务的优先级
- pxCreatedTask:用于接收任务句柄
- 以下是该函数的解析
- 声明了一个TCB指针用于存放待会创建后的TCB数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE uxStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
TCB_t * pxNewTCB;
BaseType_t xReturn;
traceENTER_xTaskCreate( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask );
pxNewTCB = prvCreateTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask ); // 调用`prvCreateTask`创建任务,并将参数传递
if( pxNewTCB != NULL ) // 判断任务是否创建成功
{
{
/* Set the task's affinity before scheduling it. */
// 设置任务的所使用的核心,默认则是使用所有核心
pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY;
}
prvAddNewTaskToReadyList( pxNewTCB ); // 调用`prvAddNewTaskToReadyList`将创建的任务添加到就绪列表,待调度器进行调度
xReturn = pdPASS;
}
else // 创建失败
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; // 返回内存不足
}
traceRETURN_xTaskCreate( xReturn );
return xReturn; // 返回任务创建状态码,若TCB创建成功则返回1表示成功,否则为-1表示内存不足以存放TCB数据
}
prvCreateTask
创建任务核心
- 若开启了动态创建任务则使用函数,否则编译器将其代码优化以用于裁剪压缩
- 根据栈的增长方向的不同,进行任务栈和TCB内存块的分配。
- 若分配失败,则回收内存防止OOM(Out Of Memory)
- 若分配成功,调用任务初始化函数对任务进行初始化操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
static TCB_t * prvCreateTask( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE uxStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
TCB_t * pxNewTCB;
/* If the stack grows down then allocate the stack then the TCB so the stack
* does not grow into the TCB. Likewise if the stack grows up then allocate
* the TCB then the stack.
首先判断栈的增长方向,若大于0则向上增长,小于0为向下增长
若栈为向上增长,先分配TCB内存块再分配任务栈内存块,栈帧模型如下
低地址 → +-----------------+
| TCB | ← 先分配,在低地址侧
+-----------------+
| 栈 | ← 后分配,栈从这里开始,向高地址生长
高地址 → +-----------------+
此时若栈溢出,栈会往高地址增加,不会影响核心数据TCB
若栈为向下增长时,先分配栈内存块再分配TCB内存块,栈帧模型如下
高地址 → +-----------------+
| 栈 | ← 栈从这里开始,向低地址生长
+-----------------+
| TCB | ← 后分配,在栈的低地址侧
低地址 → +-----------------+
TCB在栈的下方,即TCB地址比栈低。若此时发生了栈溢出会影响到TCB的数据,但OS内部有栈溢出检测
*/
{
/* Allocate space for the TCB. Where the memory comes from depends on
* the implementation of the port malloc function and whether or not static
* allocation is being used. */
/* MISRA Ref 11.5.1 [Malloc memory assignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
/* coverity[misra_c_2012_rule_11_5_violation] */
// 分配内存块用于存放TCB
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL ) // 若为NULL则内存分配失败,反之
{
( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
/* Allocate space for the stack used by the task being created.
* The base of the stack memory stored in the TCB so the task can
* be deleted later if required. */
/* MISRA Ref 11.5.1 [Malloc memory assignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
/* coverity[misra_c_2012_rule_11_5_violation] */
pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
if( pxNewTCB->pxStack == NULL ) // 同上
{
/* Could not allocate the stack. Delete the allocated TCB. */
vPortFree( pxNewTCB ); // pxNewTCB->pxStack为NULL则说明内存分配失败,回收内存防止OOM
pxNewTCB = NULL;
}
}
}
{
StackType_t * pxStack;
/* Allocate space for the stack used by the task being created. */
/* MISRA Ref 11.5.1 [Malloc memory assignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
/* coverity[misra_c_2012_rule_11_5_violation] */
pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
if( pxStack != NULL )
{
/* Allocate space for the TCB. */
/* MISRA Ref 11.5.1 [Malloc memory assignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
/* coverity[misra_c_2012_rule_11_5_violation] */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
/* Store the stack location in the TCB. */
pxNewTCB->pxStack = pxStack;
}
else
{
/* The stack cannot be used as the TCB was not created. Free
* it again. */
vPortFreeStack( pxStack ); // pxNewTCB为NULL则说明内存分配失败,释放掉TCB和任务栈内存块
}
}
else
{
pxNewTCB = NULL;
}
}
if( pxNewTCB != NULL )
{
{
/* Tasks can be created statically or dynamically, so note this
* task was created dynamically in case it is later deleted. */
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; // 设置任务静态标志为0表示动态分配的TCB和栈内存块
}
prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); // 调用`prvInitialiseNewTask`初始化任务
}
return pxNewTCB;
}
prvInitialiseNewTask
任务初始化
- 首先判断portUSING_MPU_WRAPPERS(内存保护单元)是否为1,即开启了内存保护单元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE uxStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
TCB_t * pxNewTCB,
const MemoryRegion_t * const xRegions )
{
StackType_t * pxTopOfStack;
UBaseType_t x;
/* Should the task be created in privileged mode? */
BaseType_t xRunPrivileged;
// 若开启了MPU,判断其任务是否需要运行在特权模式,当运行在特权模式xRunPrivileged为pdTRUE,否则为pdFALSE
if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )
{
xRunPrivileged = pdTRUE;
}
else
{
xRunPrivileged = pdFALSE;
}
uxPriority &= ~portPRIVILEGE_BIT; // 清除最高位特权模式标志位,用于获取任务实际的优先级
// 判断是否提供了memset依赖
{
/* Fill the stack with a known value to assist debugging. */
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) uxStackDepth * sizeof( StackType_t ) ); // 初始化内存块数据
}
/* Calculate the top of stack address. This depends on whether the stack
* grows from high memory to low (as per the 80x86) or vice versa.
* portSTACK_GROWTH is used to make the result positive or negative as required
* by the port.
计算栈顶地址,需要根据栈的增长方向进行不同的操作
*/
{
pxTopOfStack = &( pxNewTCB->pxStack[ uxStackDepth - ( configSTACK_DEPTH_TYPE ) 1 ] ); // 设置栈的最后一个元素为栈顶
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); // 对栈地址进行字节对齐,字节对齐大小根据不同的平台架构设定,具体根据硬件的数据总线的数据宽度设定。假设使用的是Cortex-M3架构则需要进行4字节对齐,没对齐前的栈地址是0x20000006,对齐后0x20000004
/* Check the alignment of the calculated top of stack is correct. */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0U ) ); // 对对齐后的栈地址进行检测断言
// 是否启用了记录栈底地址,若启用了则设置pxEndOfStack
{
/* Also record the stack's high address, which may assist
* debugging. */
pxNewTCB->pxEndOfStack = pxTopOfStack;
}
}
{
pxTopOfStack = pxNewTCB->pxStack; // 栈顶即为栈的首地址
pxTopOfStack = ( StackType_t * ) ( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) + portBYTE_ALIGNMENT_MASK ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); // 栈地址字节对齐
/* Check the alignment of the calculated top of stack is correct. */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0U ) );
/* The other extreme of the stack space is required if stack checking is
* performed. */
pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( uxStackDepth - ( configSTACK_DEPTH_TYPE ) 1 ); // 记录栈底
}
/* Store the task name in the TCB. */
if( pcName != NULL ) // 任务是否有名称
{
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) // 将名称从临时内存复制到TCB内存块里
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
/* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than
* configMAX_TASK_NAME_LEN characters just in case the memory after the
* string is not accessible (extremely unlikely). */
if( pcName[ x ] == ( char ) 0x00 )
{
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* Ensure the name string is terminated in the case that the string length
* was greater or equal to configMAX_TASK_NAME_LEN. */
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1U ] = '\0';
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* This is used as an array index so must ensure it's not too large. */
configASSERT( uxPriority < configMAX_PRIORITIES ); // 优先级断言,若优先级超过最大优先级则断言
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) // 若超过了最高优先级
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; // 设置优先级为最高优先级
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxNewTCB->uxPriority = uxPriority; // 将优先级记录到结构体里
{
pxNewTCB->uxBasePriority = uxPriority; // 是否使用了互斥锁,保留原有的优先级用于互斥锁继承机制
}
vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); // 初始化任务状态链表结点
vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); // 初始化事件状态链表结点
/* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get
* back to the containing TCB from a generic item in a list. */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); // 设置链表结点的拥有者为pxNewTCB
/* Event lists are always in priority order. */
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); // 事件节点值为最高优先级减除当前的优先级,对其反向映射,越高的优先级此值越小
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); // 设置链表结点的拥有者为pxNewTCB
{
vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, uxStackDepth ); // 若启用了MPU,设置允许访问的内存区域
}
{
/* Avoid compiler warning about unreferenced parameter. */
( void ) xRegions;
}
{
/* Allocate and initialize memory for the task's TLS Block. */
configINIT_TLS_BLOCK( pxNewTCB->xTLSBlock, pxTopOfStack ); // 设置TLS块
}
/* Initialize the TCB stack to look as if the task was already running,
* but had been interrupted by the scheduler. The return address is set
* to the start of the task function. Once the stack has been initialised
* the top of stack variable is updated. */
{
/* If the port has capability to detect stack overflow,
* pass the stack end address to the stack initialization
* function as well. */
{
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) );
}
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) );
}
}
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) );
}
}
{
/* If the port has capability to detect stack overflow,
* pass the stack end address to the stack initialization
* function as well. */
{
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters );
}
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters );
}
}
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); // 初始化任务的堆栈
}
}
/* Initialize task state and task attributes. */
{
pxNewTCB->xTaskRunState = taskTASK_NOT_RUNNING; // 让任务没运行在任何核心上
/* Is this an idle task? */
if( ( ( TaskFunction_t ) pxTaskCode == ( TaskFunction_t ) prvIdleTask ) || ( ( TaskFunction_t ) pxTaskCode == ( TaskFunction_t ) prvPassiveIdleTask ) ) // 是否是空闲任务, 如果是设置任务空闲属性标志位
{
pxNewTCB->uxTaskAttributes |= taskATTRIBUTE_IS_IDLE;
}
}
if( pxCreatedTask != NULL )
{
/* Pass the handle out in an anonymous way. The handle can be used to
* change the created task's priority, delete the created task, etc.*/
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB; // 任务句柄
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
pxPortInitialiseStack
初始化栈
- 此函数是任务的核心,用于初始化堆栈模拟任务已经被执行了但被调度器中断了
- 以下是GCC ARM_CM4F的函数分析,不同的架构有不同的函数,但其内容大同小异
- 压入了部分数据,并分配了部分栈内存用于存放任务的寄存器数据,供调度器下次调度的时候恢复寄存器确保任务的正常调度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65/*
* See header file for description.
*/
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
/* Simulate the stack frame as it would be created by a context switch
* interrupt. */
/* Offset added to account for the way the MCU uses the stack on entry/exit
* of interrupts, and to ensure alignment. */
pxTopOfStack--; // 模拟栈sp--
*pxTopOfStack = portINITIAL_XPSR; // 将数据压入栈 /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; // 程序计数器Program Counter /* PC */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; // Link Register /* LR */
/* Save code space by skipping register initialisation. */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
/* A save method is being used that requires each task to maintain its
* own exec return value. */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_EXC_RETURN;
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}
prvAddNewTaskToReadyList
添加任务到就绪列表
- 此函数分为单核和多核两个,这里以单核为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
{
/* Ensure interrupts don't access the task lists while the lists are being
* updated. */
taskENTER_CRITICAL();
{
uxCurrentNumberOfTasks = ( UBaseType_t ) ( uxCurrentNumberOfTasks + 1U );
if( pxCurrentTCB == NULL ) // 当前TCB是否为空,为空则表示当前没有任何任务执行,目前的任务是第一个任务
{
/* There are no other tasks, or all the other tasks are in
* the suspended state - make this the current task. */
pxCurrentTCB = pxNewTCB; // 设置新建的任务的TCB为当前TCB
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
/* This is the first task to be created so do the preliminary
* initialisation required. We will not recover if this call
* fails, but we will report the failure. */
prvInitialiseTaskLists(); // 若没有任务在列表里,则初始化任务列表
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* If the scheduler is not already running, make this task the
* current task if it is the highest priority task to be created
* so far. */
if( xSchedulerRunning == pdFALSE )
{
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) // 当前TCB存在表示当前有任务正在运行,若当前任务的优先级小于新任务则直接抢占将新任务设置成当前的任务
{
pxCurrentTCB = pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
uxTaskNumber++; // 任务数递增
{
/* Add a counter into the TCB for tracing only. */
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
traceTASK_CREATE( pxNewTCB );
prvAddTaskToReadyList( pxNewTCB ); // 插入任务到对应优先级的链表中
portSETUP_TCB( pxNewTCB );
}
taskEXIT_CRITICAL();
if( xSchedulerRunning != pdFALSE )
{
/* If the created task is of a higher priority than the current task
* then it should run now. */
taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxNewTCB ); // 如果当前任务优先级小于新任务优先级则立刻触发PendSV中断进行任务调度,让其立刻执行新任务
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}prvAddTaskToReadyList
插入任务到链表中 - 更新当前的最高优先级
- 插入TCB到对应优先级索引的链表中
1
2
3
4
5
6
7
8
9
10
11
12
13
do { \
traceMOVED_TASK_TO_READY_STATE( pxTCB ); \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ); \
} while( 0 )
任务调度分析
xPortSysTickHandler
OS心跳分析
- 此函数在移植OS的时候需要周期性调用以给予OS时钟用于分片进行任务调度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49void xPortSysTickHandler( void )
{
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
* executes all interrupts must be unmasked. There is therefore no need to
* save and then restore the interrupt mask value as its value is already
* known. */
portDISABLE_INTERRUPTS(); // 禁用所有中断,以防止操作被中断打断
traceISR_ENTER();
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE ) // 调用函数`xTaskIncrementTick`对时钟Tick进行递增
{
traceISR_EXIT_TO_SCHEDULER();
/* A context switch is required. Context switching is performed in
* the PendSV interrupt. Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
else
{
traceISR_EXIT();
}
}
portENABLE_INTERRUPTS(); // 恢复中断接收来自外部或内部的中断
}
xTaskIncrementTick
时钟核心
1 | BaseType_t xTaskIncrementTick( void ) |
参考文献
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Jamie793’ S Blog!
评论