皮皮网

【微营销源码】【linux内核源码分析】【unix 网络编程 源码】os ticklist源码

时间:2024-11-28 21:11:46 来源:防砍柴网源码 作者:五线谱副图公式源码

1.如何学习 nucleus os

os ticklist源码

如何学习 nucleus os

       å†…容:

       ä¸€ã€nucleus plus特点:

           1.内核采用微内核的设计,方便移植,资料写着更reliable,但是我不这么认为,与linux相比,以ARM平台为例,NU只用到了SVC mode,内核与用户任务都运行在同一个状态下,也就是说所有的task都拥有访问任何资源的权限,这样很reliable么?

           2.real-time OS,NU是一个软实时操作系统(VxWorks是硬实时),thread control component支持多任务以及任务的抢占,对于中断的处理定义了两种服务方式,LISR和HISR,这个与linux中的上、下半部机制类似,linux中的下半部是通过软中断来实现的,NU的HISR只是作为一种优先级总是高于task的任务出现。

           3.NU是以library的方式应用的,通过写自己的app task与裁剪后的NU内核及组件链接起来,NU并没有CLI

       äºŒã€ç»„件

       1.IN component

           初始化组件由三个部分组成,硬件在reset后首先进入INT_initialize(),进行板级的相关初始化,首先设置SVC mode,关中断,然后将内核从rom中拷贝至ram中,建立bss段,依次建立sys stack,源码微营销源码 irq stack和fiq stack,最后初始化timer,建立timer HISR的栈空间,看了一下平台的代码,一个tick大概是.8ms,完成板级的初始化后就进入了INC_initialize,初始化各个组件,其中包括Application initialize,create task和HISR,最后将控制权交给schedule,主要看了一下RAM中地址空间的安排

       |timer HISR stack = |

       |FIQ stack = |

       |IRQ stack = |

       |SVC stack = |

       |.bss|

       |.data|

       |.text|

       å…¶ä¸­SVC stack的大小与中断源的个数相关,nested irq发生时,irq_context保存在SVC stack中,IRQ的stack只是做了临时栈的作用。

       2.thread control component

           TC组件是NU内核的最重要组成部分,主要涵盖了调度、中断、任务的相关操作、锁、时钟几个方面,下面分别介绍。

       è°ƒåº¦ï¼ˆschedule)

           NU中的线程类型(在同一个地址空间内)有两种,HISR和task,HISR可以理解为一种优先级较高的task,但又不是task,HISR优先级高于task的实现方式就是schdule时,先去查看当前是否有active的HISR,再去查看task。task有suspend、ready、finished和terminated四种状态,而HISR只有executing和no-active这两种状态。

           每一个task都有一个线程控制的数据结构(TCB thread control block),其中包括了task的优先级、状态、时间片、task栈、protect信息、signal操作的标志位和signal_handler等,task在创建时初始化这些信息,将task挂到一个create_list上,初始设定task为pure_suspend,如果设定auto start,调用resume_task()唤醒task,这里有个细节,如果在application initialize中create_task(),则task不会自动运行,因为初始化还未完成,控制权还没有交给schedule,无法调度task。task被唤醒后状态改变为ready,并挂在一个TCD_Priority_List[]上,数组的每个元素是一个指向TCB环形双向链表的指针,根据task的tc_priority找到对应优先级的TCB head pointer。

                                  

           每一个HISR都有一个HISR控制的数据结构(HCB HISR control block),其中只有优先级,HISR栈和HISR entry信息,因此HISR是不可以suspend,同时也没有time slice以及signal的相关操作,一般情况下当发生了中断后,HISR被activate,schedule就会调度HISR运行,期间如果不发生中断,HISR的执行是不会被打断的,HISR的优先级只有0、1、2,timer的HISR优先级为2,也就是说由外部中断激活的HISR很难被抢占的,只有更高优先级的中断HISR才可以。与task不同,被激活的HISR使用head_list和tail_list将HCB挂在一个单项的链表上,因为相同优先级的HISR不会抢占对方,因此不需要双向链表,使用两个指针目的是加快HISR执行的速度。

           一个实时操作系统的核心就是对于任务的调度,NU的调度策略是time slice和round robin的算法,

       è°ƒåº¦çš„部分主要有三个函数control_to_system()用于保存上下文,建立solicited stack,关中断,关system time slice,并重置task的time slice为预设值,将sp更新为system_stack_pointer,调用schedule(),调度的过程是非常简单的查询,就是查看两个全局的变量,TCD_Execute_HISR和TCD_Execute_Task,schedule部分的关键是打开了中断,不然如果当前没有ready的task或是被激活的HISR,则shedule死循环下去,查询到下一个应该执行的线程后跳转至control_to_thread(),在这里重新开启system time slice,然后将线程的tc_stack_ptr加入到sp中,切换至线程的栈中,依次pop出来,即完成了任务调度。

           任务的切换主要是上下文的切换,也就是task栈的切换,函数的调用会保存部分regs和返回地址,这些动作都是编译器来完成的,而OS中的任务切换是运行时(runtime)的一种状态变化,因此编译器也无能为力,所以对于上下文的保存需要代码来实现。

           任务的抢占是异步的因此必须要通过中断来实现,一般每次timer的中断决定当前的task的slice time是否expired,然后设置TCT_Set_Execute_Task为相同优先级的其他task或更高优先级的task;高优先级的task抢占低优先级的task,一般是外部中断触发,在HISR中resume_task()唤醒高优先级的task,然后schedule到高优先级的task中,因为timer的HISR是在系统初始化就已经注册的,只是执行timeout和time slice超时后的操作,并没有执行resume_task的动作。

           NU中的stack有两种solicited stack和interrupt stack,solicited stack是一种minmum stack,而interrupt stack是对当前所有寄存器全部保存,TCB中的minimum stack size = 申请得到stack size - solicited stack(在arm mode下占字节,thumb mode下占字节),thumb标志用来记录上下文保存时的ARM的工作模式,c代码编译为thumb模式,这样可以减小code size,提高代码密度,assembly代码编译为arm模式提升代码的效率,NU中内核的代码不多,主要是assembly代码。stack的类型与其中PC指向的shell无关,interrupt stack保存的是task或是HISR在执行的过程中被中断时的现场,solicited stack建立的地方包括 control_to_system()、schedule_protect()和send_signals()发送给占有protect资源的task的情况,HISR_Shell()执行完后会建立solicited stack,再跳转至schedule。

       (Lower Address) Stack Top -> 1 (Interrupt stack type)

       CPSR Saved CPSR

       r0 Saved r0

       r1 Saved r1

       r2 Saved r2

       r3 Saved r3

       r4 Saved r4

       r5 Saved r5

       r6 Saved r6

       r7 Saved r7

       r8 Saved r8

       r9 Saved r9

       r Saved r

       r Saved r

       r Saved r

       sp Saved sp

       lr Saved lr

       (Higher Address) Stack Bottom-> pc Saved pc

       (Lower Address) Stack Top -> 0 (Solicited stack type)

       !!FOR THUMB ONLY!! 0/0x Saved state mask

       r4 Saved r4

       r5 Saved r5

       r6 Saved r6

       r7 Saved r7

       r8 Saved r8

       r9 Saved r9

       r Saved r

       r Saved r

       r Saved r

       (Higher Address) Stack Bottom-> pc Saved pc

       ä¸€ä¸ªç®€å•çš„例子说明stack的情况,首先是一个task在ready(executing)的状态下,而且time slice超时了,timer中断发生后,保存task上下文interrupt_contex_save(),在task的tc_stack_ptr指向的地方建立中断栈

       taskA    |interrupt stack|___tc_stack_ptr 栈顶端是pc=lr-4

       ARM对于中断的判定发生在当前指令完成execute时,同时pipeline的原因pc=pc+8,入栈时就把lr-4首先放在stack的最高端(high)。

       timer的LISR完成后激活了HISR,执行TCC_Time_slice()将当前task移到相同优先级的尾端,并且设置下一个要执行的task,HISR在栈顶端保存的是这个HISR_shell的入口地址,因为task的执行完就finished,HISR是可重入的

       HISR     |solicited stack|  æ ˆé¡¶ç«¯æ˜¯HISR_shell_entry

       ä¸­æ–­ï¼ˆinterrupt)

       å‰é¢å·²ç»æåŠäº†ä¸­æ–­çš„基本操作,这里就写一些代码路径的细节,中断的执行主要是两个部分LISR和HISR,分成两个部分的目的就是将关中断的时间最小化,并且在LISR中开中断允许中断的嵌套,以及建立中断优先级,都可以减少中断的延迟,保证OS的实时性。

       NU的中断模式是可重入的中断处理方式,也就是基于中断优先级和嵌套的模式,中断的嵌套在处理的过程中应对lr_irq_mode寄存器进行保存,因为高优先级的中断发生时会覆盖掉低优先级中断的r和spsr,因此要利用系统的栈来保存中断栈。

       NU对于中断上下文的保存具体操作如下:

       ï¼ˆ1)在中断发生后执行的入口函数INT_IRQ()中,将r0-r4保存至irq的栈中

       ï¼ˆ2)查找到对应的interrupt_shell(),clear中断源,更新全局的中断计数器,然后进行interrupt_contex_save

       ï¼ˆ3)首先利用r1,r2,r3保存irq模式下的sp,lr,spsr,这里sp是用来切换至系统栈后拷贝lr和spsr的,这里保存lr和spsr是目的是task被抢占后,当再次schedule时可以返回task之前的状态。

       ï¼ˆ4)切换至SVC模式,如果是非嵌套的中断则保存上下文至task stack中,将irq模式下的lr作为顶端PC的返回值入栈,将SVC模式下的r6-r入栈,将irq模式下的sp保存至r4中入栈,最后将保存在irq_stack中的r0-r4入栈

       ï¼ˆ5)如果是嵌套中断,中断的嵌套发生在LISR中,在执行LISR时已经切换至system stack,因此嵌套中断要将中断的上下文保存至system stack中,与task stack中interrupt stack相比只是少了栈顶用来标记嵌套的标志(1 not nested)

       ï¼ˆ6)有一个分支判断,就是如果当前线程是空,即TCD_Current_Thread == NULL,表明当前是schedule中,因为初始化线程是关中断的,这样就不为schedule线程建立栈帧,因为schedule不需要保存上下文,在restore中断上下文时直接跳转至schedule。

       ä¸­æ–­ä¸Šä¸‹æ–‡çš„恢复

       å…¨å±€çš„中断计数器INT_Count是否为0来判定当前出栈的信息,如果是嵌套则返回LISR中,否则切换至system stack执行schedule

       timer

       timer与中断紧密相关,其实timer也是中断的一种,只是发生中断的频率较高,且作用重大,一个实时操作系统,时间是非常重要的一部分,NU中的timer主要有四个作用:

       ï¼ˆ1)维护系统时钟 TMD_system_clock

       ï¼ˆ2)task的time slice

       ï¼ˆ3)task的suspend timeout timer

       ï¼ˆ4)application timer

       å…¶ä¸­ï¼ˆ3)(4)共用一种机制,一个全局的时间轴TMD_timer,timeout timer和app timer都建立在一个TM_TCB的数据结构上,通过tm_remaining_time来表征当前timer的剩余时间,例如当前有timer_list上有三个TM_TCB,依次是Ta = 5,Tb = 7, Tc = ,那么建立的链表上剩余时间依次是5,2,8,如果现在要加入一个新的timer根据timer值插入至合适的位置,如果插入的timer为,则安排在Tb后面,剩余时间为1,后面的8改为7,当发生了timer expired,则触发timer_HISR,如果是app timer则执行timer callback,如果是task timeout timer,则执行TCC_Task_Timeout唤醒task。

       ï¼ˆ2)的实现也是依赖于全局的time slice时间轴,每一个task在执行时都会将自己的时间片信息更新至全局的时间轴上,当一个task的time slice执行完在timer HISR中调用TCC_task_Timeout将当前的task放在相同优先级list的最尾端,并设置下一个最高优先级的任务。task在执行的过程中只有被中断后time slice会保存下来,其他让出处理器的情况都会将time slice更新为预设值。

       protect

       protect与linux的锁机制类似,互斥访问,利用开关中断来实现,并且拥有protect的task是不可以suspend的,必须要将protect释放后才可以挂起,当一个优先级较低的task占有protect资源,如果被抢占,一个高优先级的task或HISR在请求protect资源时会执行TCC_schedule_protect()让出处理器给低优先级的task执行,直到低优先级的task执行unprotect()为止,此时task或HISR建立的是solicited stack,同时在control_to_thread前开关中断一次,这样可以减少一次上下文的切换。NU中常用到的是system_protect,它就是一把大锁,保护内核中所有全局数据结构的顺序访问,粒度很大。

       LISR中不可以请求protect资源,因为LISR是中断task后执行,如果task占有protect资源,这时LISR又去请求protect资源,会发生死锁,因为LISR让出处理器后,schedule没办法再次调度到LISR执行,则发生死锁错误,因此在LISR中除了activate_HISR()以外不可以使用system call,例如resume_task等等,这写系统调用都会请求protect资源。

       å¯¹äºŽprotect的请求按照一定的顺序可以防止死锁,NU的源码中一般将system_protect资源的请求放在后面,其他如DM_protect先请求。

关键词:wow注册源码

copyright © 2016 powered by 皮皮网   sitemap