关于nucleus plus的学习笔记
扫描二维码
随时随地手机看文章
signal 信号是异步通知task的一种机制,HISR是不可以接收信号的,但是可以发送信号。
TCB中与signal相关数据结构包括active_signal,enable_signal(这是一个掩码,如果为0则不执行signal_handler),(*signal_handler),主要有两个函数,一个是send_signals()和signal_shell(),其中send_signals()函数主要是区分发送信号给自己还是给其他的task,如果发给自己就直接执行signal_shell(),如果发送至其他task(处于suspend或ready但没有占用处理器的状态),target task必须要释放占用的protect,因为在signal_handler中可能会去请求protect资源,这样就会发生死锁,当前task执行TCT_protect_switch()函数,与请求protect()函数前半部分一致,调用schedule_protect()让target task释放占用的protect资源。
在target stack处建立solicited stack,其中PC = &signal_shell,保存target task的status和tc_statck_ptr,根据当前的状态,如果是ready或pure_suspend,则返回,否则就resume_task()唤醒target task,根据返回值确定是否进入schedule()。
在send_signal中应用protect来保护共享数据,而到了protect_switch中应用关中断来保护,中间有一个数据保护的真空期,因此要两次确认target task没有再占用protect资源
suspend/resume
对于task状态改变的操作主要有suspend和resume函数,其中suspend将一个task挂起,当挂起自己时会control_to_system,finished和terminated状态的转变也是利用suspend函数,resume函数用来将task唤醒,并返回一个标志是否需要context switch
线程同步
应用里可以使用到的线程同步有两种方式,semaphore和event groups,semaphore与linux中的一直,但是NU中并没有对优先级反转有处理
event groups是一个32bit的数据结构,可以标记32个事件,操作包括set和retrieve,set event时会唤醒suspend list上的所有task,最后在对相应bit consume,retrieve去请求event,如果失败则挂起在suspend list上,只有拿到event后才会对consume
线程通信
NU中支持的线程通信方式包括queue,pipe,mailbox,这里queue和pipe的机制基本相同,只是queue是按32bit操作,pipe是按照字节访问,但是代码里也做了对齐,mailbox是一种信箱机制,信箱的大小为4*32bit,使用的好处就是执行速度快
内存管理
NU并没有使用到虚拟内存管理,直接访问物理地址,有两种建立内存池的方法,动态内存管理和静态内存管理,动态内存管理采用的分配策略是first-fit和释放后内存融合,静态内存管理是每次分配固定大小的内存块,这样可能会引起内存的利用率降低,但是不会引起内存外部碎片
1. 中断向量表的reset地址为0x0,直接跳转至INT_Initialize()
2. Low_bit_set[256]用来快速计算优先级的表,其中放的是对应优先级数值第一个不为0的数,例如当前最高priority=17,则low_bit_set[17] = 0,17 = 0x11第一个不为0的位是第0位
3. HISR不会suspend,没有time slice即同优先级的HISR依次执行,不会接收信号,只可以被中断,如果激活了更高优先级的HISR则当前HISR被抢占。
4. 当中断发生时,ARM硬件完成的工作包括:
i. 保存中断前的CPSR至SPSR_irq_mode
ii.保存中断时PC值至lr_irq_mode
iii.切换至irq mode,屏蔽irq bit,set arm mode
iv. 将PC指向irq_entry(0x18)
5. time slice只有在被中断里才会保存下来,即一个task被中断后执行了LISR和HISR后,重新schedule到task,其time slice是之前剩下的时间片,当task主动让出处理器时,比如self-suspend,send_signal,resume高优先级的task时,会将time slice重新置为预设值
5. 在control_to_system里有clear protect的动作
6. TMD_Timer_Start记录的是当前timer_active_list上顶端timer的remaining_time,当发生start_timer,stop_timer或timer expired时都会去更新这个值
7. 所有的system call都要用system_protect来保护,因为其中有对于重要全局变量的访问
8. TCB中有一个suspend_protect,用来记录task在suspend前拥有的是什么样类型的protect,例如一个请求Dynamic memory的task suspend在dm_suspend_list上,在挂起前将dm_protect保存至suspend_protect中,因为一个task在suspend前必须释放所持有的protect,所以只能利用suspend_protect标记之前占有的是dm_protect,当timeout发生时,重新获得dm_protect去执行cleanup函数,做清理工作将task从dm_suspend_list上移除,resume一个task时会将suspend_protect清空。
9. 一个HISR中可以send_signal,suspend其他task,请求protect资源,这些动作都有可能使HISR让出处理器给低优先级的task去执行,都是调用TCC_schedule_protect(),目的是避免deadlock
10. deadlock产生的四个必要条件 (必考)
i.互斥条件:资源同一时间只可以被一个进程访问
ii.请求与保持条件:当进程请求新资源阻塞时,不释放已经占有的资源
iii.不剥夺条件:进程在占有资源时,未使用完之前,不可以被强行剥夺
iv. 循环等待条件:若干进程形成一种头尾相接的循环等待资源关系
12. 优先级反转(priority inversion)(必考)
产生原因:三个task,优先级关系是taskA>taskB>taskC,taskC占有互斥sem,当taskA执行时申请sem,suspend,此时taskB被唤醒,继续执行,则taskC无法释放sem,导致高优先级的taskA无法执行,而低优先级的taskB一直执行下去
解决方法:
i.disable interrupt (影响OS的实时性)
ii.disable preemption (影响实时性)
iii.优先级继承
在运行时,taskA请求低优先级taskC已经占有的sem,则将taskC的优先级提升至taskA相同
iv. 优先级天花板
在未运行的情况下,首先估计会使用到sem的task,然后将所有的task优先级升高至这些task中的最高 优先级。
11. 在TCT_schedule_Protected的代码中,在control_to_thread前开关了一次中断,这样可以节省一次上下文切换,让中断在当前task上发生,而不是切换至占有protect的task在发生中断。
12. 当task被中断后抢占,不会释放protect资源,因此在LISR中不可以使用与Protect相关的系统调用,只可以使用activate_HISR(),它是用中断来保证原子访问的。
13. protect的嵌套利用unprotect_specific()和set_current_protect()来实现,同时system_protect一般作为第二个protect去申请,因为system_protect保护了内核中全局的数据结构,粒度较大,因此要尽量减少持有的时间
14. 第一次中断的上下文保存在task stack,嵌套的中断上下文保存在system stack,因为中断嵌套发生在LISR中,此时sp值已经更新为system stack ptr
15. protect() send_signal(), suspend()都有为了防止deadlock让持有protect的task执行,直到释放protect的机制
16. 在调用terminate task时,如果task是suspend要调用cleanup函数,要利用suspend_protect保护,同时不会与system_protect形成死锁
17. queue和pipe,按照FIFO的模式发送message,在变长模式下,如果taskA的message较大suspend,则taskB即使有空间也必须suspend在taskA后面。
18.resume返回true的条件
i.当前priority_list上只有target_task一个
ii.target_task的优先级高于TCD_Highest_priority
iii.确定当前task是可抢占的
iv. 当前线程是一个task,如果是NULL则是在初始化里面,初始化还未完成不应该schedule,如果在 HISR里,因为HISR的优先级总是高于task的,因此也不会发生抢占
19. Dynamic memory的数据结构使用双向链表是为了可以进行融合操作
20. first-fit策略必须使用相邻融合的内存管理方法,但是仍会造成50%的浪费
21. TCD_current_thread == NULL,如果当前开着中断则是schedule,如果是关中断则是INT_initialize
22. 如果申请protect,TCD_current_thread == NULL,protect中直接跳过,因为这是在初始化中,初始化使用关中断来保护的
23.如果一个HISR suspend了,则下一次LISR触发HISR时,就无法处理了