切换线程时需要保存什么
- 函数需要保存吗?函数在Flash上,不会被破坏,无需保存。
- 函数执行到了哪里?需要保存吗?需要保存。
- 全局变量需要保存吗?全局变量在内存上,无需保存。
- 局部变量需要保存吗?局部变量在栈里,也是在内存里,只要避免栈不会被破坏即可,局部变量无需保存。
- 运算的中间值需要保存吗?中间值保存在CPU寄存器里,另一个线程也需要用到寄存器,所以CPU寄存器要保存。
- 函数运行到了哪里:也是一个CPU寄存器,名为“PC”。
总结:CPU寄存器需要保存,保存在线程的栈里。
ARM架构
ARM芯片属于精简指令计算机(RISC:Reduced Instruction Set Computor),它所用的指令比较简单,有如下特点:
- 对内存只有读、写指令。
- 对于数据的运算都是在CPU内部实现。
- 使用RISC指令的CPU复杂度小一点,易于设计。
几条汇编指令
- 读内存指令:LDR,即Load之意。
- 写内存指令:STR,即Store之意。
- 加减指令:ADD、SUB
- 跳转:BL,Branch And Link
- 入栈指令:PUSH
- 出栈指令:POP
加载指令LDR:LDR r0,[addrA],意思是将地址addrA的内容加载到r0里面。
存储指令STR:STR r0,[addr A],把r0的值存储到地址addrA上。
加法运算指令(ADD):ADD r0,r1,r2,意思为r0=r1+r2。
减法运算指令(SUB):SUB r0,r1,r2,意思为r0=r1-r2。
寄存器入栈/出栈指令(PUSH/POP)
寄存器入栈(PUSH):PUSH {r3, lr}意思是将寄存器r3和lr写入内存栈。
- 本质是写内存STR指令,高标号寄存器写入高地址的栈里,低标号寄存器写入低地址的栈里。
- lr即r14,写入地址为sp-4的内存,然后sp=sp-4。
- r3,写入地址为sp-4的内存,然后sp=sp-4。
寄存器出栈指令(POP):POP {r3,pc}意思是取出内存栈的数据放入r3和pc中。
- 本质是读内存LDR指令,高标号寄存器的内容来自高地址的栈,低标号寄存器的内容来自低地址的栈。
- 读地址为sp的内容存入r3,然后sp=sp+4。
- 读地址为sp的内容存入pc,然后sp=sp+4。
寄存器别名
PUSH指令=多次调用STR指令,并且会调整SP的值。
BL A:会记录返回地址,保存在R14里,然后跳转到A执行,执行完后,PC会跳转到R14所指向的地址。
初始化栈
IPC
引入IPC,主要是为了两项功能:
- 线程间互斥
- 休眠-唤醒机制
队列
队列里有多个消息块,消息块里面可以存储消息(数据),每个消息块大小相同。
队列里有两个链表:Sender List、Receiver List。
挂起线程也就是将线程从就绪链表中移出。
再将线程挂到队列的等待链表中。
启动定时器。
互斥量如何实现
对于互斥变量a
- 关中断
- 汇编指令:原子地修改a
IPC
消息队列的读取都是使用memcpy()。
邮箱的读取直接赋值即可。
链表、定时器、环形缓冲区(读、写位置)
信号量的缺点:
- 谁都可以释放信号量
- 优先级反转
Mutex解决
- 谁拥有谁释放
- 优先级继承
一个互斥量可以被同一个线程多次take
信号不是信号量,是一种异步通知机制。
**信号就是线程的软件中断。 **
信号处理流程
信号就是线程的“软件中断”,跟“硬件中断”类似:
要安装信号处理函数,相当于给硬件中断提供处理函数。
/* thread1 安装信号,自定义处理函数 */
rt_signal_install(SIGUSR1, thread1_signal_handler);
/* thread1 要使能信号,相当于使能硬件中断,解除屏蔽 */
rt_signal_unmask(SIGUSR1);
/* 向thread1发出信号,相当于触发硬件中断 */
rt_thread_kill(thread1, SIGUSR1);
什么是块设备?
有存储功能:写入数据到pos位置,再读pos可以得到一样的数据。
数据的传输单位是:扇区。
挂载
挂载点(path)
文件系统
块设备