FreeRTOS任务切换源码分析--Apple的学习笔记

一,前言

RTOS中最吸引我的地方是带汇编的任务切换,没想到我在看port.c,全部看完后,有一个xPortPendSVHandler函数觉得理解的不太清晰,但是以前我肯定理解过的,所以呢,我又调试了下,等于再复习下。

二,xPortPendSVHandler源码分析

  1. 先做过铺垫,来看下什么时候会调用xPortPendSVHandler中断函数。
    任务时间片切换#define xPortSysTickHandler SysTick_Handler,xPortSysTickHandler函数就是一个中断。在启动第一个高优先级任务前,vPortSetupTimerInterrupt函数中已经设置了心跳包的频率。portNVIC_SYSTICK_LOAD_REG = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
    #define configTICK_RATE_HZ ( ( TickType_t ) 500 )说明是500Hz,1/500=0.002就是2ms的一个心跳中断。
    心跳中断中的函数中portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;就是请求pendsv中断,并增加tick值,检查任务就绪队列中是否有任务,存在任务则请求pendsv进行任务切换。如下c代码还是很好理解的。
void xPortSysTickHandler( void )
{
    uint32_t ulPreviousMask;

    ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();
    {
        /* Increment the RTOS tick. */
        if( xTaskIncrementTick() != pdFALSE )
        {
            /* Pend a context switch. */
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }
    portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );
}

那么就说明是心跳时钟切换过程中通过portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;来设置pendsv中断请求,之后才进入xPortPendSVHandler中断函数。
2. xPortPendSVHandler源码分析
中文注释我已经直接写在函数中了。如下,这都是我经过debug单步调试验证过的。一开始没有看懂,主要是stmdb用于将寄存器压栈,ldmia用于将寄存器弹出栈等几个汇编指令忘记了含义。

__asm void xPortPendSVHandler( void )
{
    extern vTaskSwitchContext
    extern pxCurrentTCB

/* *INDENT-OFF* */
    PRESERVE8

    mrs r0, psp

    ldr r3, = pxCurrentTCB /* Get the location of the current TCB. */
    ldr r2, [ r3 ]
/* 保存现场,主要保存PSP中的r4~r11 */
    subs r0, # 32  /* Make space for the remaining low registers. */
    str r0, [ r2 ] /* Save the new top of stack. */
stmia r0 !, { r4 - r7 } /* Store the low registers that are not saved automatically. */
/* 因为thumb指令stmia只能访问r0~r7 ,所以下面r8~r11先保存到r4~r7,然后再push到psp栈中,等于腾出32自己(8个寄存器的地址空间)先push r4~r7再push r8~r11 */
    mov r4, r8 /* Store the high registers. */
    mov r5, r9
    mov r6, r10
    mov r7, r11
    stmia r0 !, { r4 - r7 }
/* 执行vTaskSwitchContext 函数前tcb和lr入栈保护 */
    push { r3, r14 }
    cpsid i
    bl vTaskSwitchContext
cpsie i
/* 恢复tcb和lr */s
    pop { r2, r3 } /* lr goes in r3. r2 now holds tcb pointer. */
    /* r2地址就是tcb指针的存储位置保存到r1 */
ldr r1, [ r2 ]
/* 将tcb内容保存到r0,tcb的内容就是通过vTaskSwitchContext运行后冲裁出待切换的tcb地址 */
ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. */
/* 从r0栈顶地址+16,腾出4个寄存器r4~r7的空间 */
adds r0, # 16  /* Move to the high registers. */
/* 将+16~+32地址中的内容先pop到r4~r7然后移动入r8~r11 */
ldmia r0 !, { r4 - r7 } /* Pop the high registers. */
    mov r8, r4
    mov r9, r5
    mov r10, r6
    mov r11, r7

    /* 临时保存下r0地址,就是栈底的地址,会保存到PSP,等bx从中断退出时候会用到*/
msr psp, r0   /* Remember the new top of stack for the task. */
    /* r0从最开始预留16以及pop后自动增加16,总共加了32,现在前去32,等于还原到栈顶,目的是之后用来恢复r4~r11 */
subs r0, # 32 /* Go back for the low registers that are not automatically restored. */
/* 然后pop r4~r7,之前popr8~r11的原因就是16 bit thumb指令只能pop r0~r7,不能直接pop 8个寄存器*/
    ldmia r0 !, { r4 - r7 } /* Pop low registers.  */
    /* 通过r3来查找返回地址,用来切换任务,返回到的是另外一个更高优先级的task函数*/
    bx r3
    ALIGN
/* *INDENT-ON* */
}

几个关键语句的调试步骤调试截图如下
stmia r0 !, { r4 - r7 }运行前。

image.png

stmia r0 !, { r4 - r7 }运行后。
将寄存器r4到r7的值依次赋值给r0指定的地址单元(0x18000460),每次赋值一次r0就加4。4个寄存器赋值完成后,R0内容(就是地址)增加了4x4=16,变成了0x18000470。
image.png

push { r3, r14 }含义为MSP入栈保护MSP地址为0x180006e8,然后将r3和r14保存到这个地址进行入栈
image.png

入栈地址是做减法,所以先查看0x180006e0地址的内容。
运行push { r3, r14 }后0x180006e0和0x180006e4地址内容保存了r3和r14的内容。进行了入栈操作。
image.png

同理pop { r2, r3 }是出栈,将r3(tcb地址)和lr通过出栈,保存到r2和r3。
image.png

adds r0, # 16运行前
image.png

adds r0, # 16运行后,r0地址变更。
image.png

4个mov后将0x180001D0后面的四个字节内容(就是自己构造栈空间中最后4个字节代表r8~r11)进行了pop。
image.png

从中断退出时候会用到。
subs r0, # 32 的含义是,r0从最开始预留16以及pop后自动增加16,总共加了32,现在前去32,等于还原到栈顶。
ldmia r0 !, { r4 - r7 } ,之前已经pop过r8~r11,现在就是pop r4r7,之前popr8r11的原因就是16 bit thumb指令只能pop r0~r7,不能直接pop这8个寄存器
image.png

bx r3就是跳入0xFFFFFFFD,说明被中断前用的是PSP的地址。0xFFFFFFF9说明用的是MSP的地址。
所以找0x180001e0的地址。要从svc中断返回的地址PC保存在0x180001e0+7*4=0x180001fc的内容,就是0x1100150D,但是thumb指令需要LSB,所以0x1100150D-1就是0x1100150C地址。bx r3从中断出来后,会跳入0x1100150C执行task。
image.png

msr psp, r0是保存r0的地址到PSP栈中,目的是把栈底保存,等bx
image.png

三,小结

通过调试及查看cortex内核手册复习了下内核寄存器的用法,这个函数就很容易理解了,看来有空我要重新看下cortexM内核手册的全部内容。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • 夜莺2517阅读 127,717评论 1 9
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,531评论 28 53
  • 兔子虽然是枚小硕 但学校的硕士四人寝不够 就被分到了博士楼里 两人一间 在学校的最西边 靠山 兔子的室友身体不好 ...
    待业的兔子阅读 2,594评论 2 9
  • 信任包括信任自己和信任他人 很多时候,很多事情,失败、遗憾、错过,源于不自信,不信任他人 觉得自己做不成,别人做不...
    吴氵晃阅读 6,186评论 4 8