1:操作系统中挂起(suspend)和阻塞(pend/block)的区别如下:

一:挂起是一种主动行为,因此恢复也应该要主动完成;而阻塞则是一种被动行为,是在等待事件或资源时任务的表现,你不知道他什么时候被阻塞(pend),也就不能确切知道他什么时候恢复阻塞。而且挂起队列在操作系统里可以看成一个,而阻塞队列则是不同的事件或资源(如信号量)就有自己的队列;
二:阻塞(pend)就是任务释放CPU,其他任务可以运行,一般在等待某种资源或信号量的时候出现。挂起(suspend)不释放CPU,如果任务优先级高就永远轮不到其他任务运行,一般挂起用于程序调试中的条件中断,当出现某个条件的情况下挂起,然后进行单步调试;
三:pend是task主动去等一个事件(信号量/锁)或消息;suspend是直接悬挂task,以后这个task和你没任何关系,任何task间的通信或者同步都和这个suspended task没任何关系了,除非你主动 resume task;
四:任务调度是操作系统来实现的,任务调度时,直接忽略挂起状态的任务,但是会顾及处于pend状态的任务,当pend下的任务等待的资源就绪后,就可以转为ready了。ready只需要等待CPU时间,当然,任务调度也占用开销,但是不大,可以忽略。可以这样理解,只要是挂起状态,操作系统就不在管理这个任务了;
五:挂起是主动的,一般需要用挂起函数进行操作,若没有resume的动作,则此任务一直不会ready。而阻塞是因为资源被其他任务抢占而处于休眠态。两者的表现方式都是从就绪态里“清掉”,即对应标志位清零,只不过实现方式不一样

操作系统中挂起、睡眠、阻塞的区别形象解释:你控制了一个雇工为你干活。你对雇工的控制是通过编程来实现的。
挂起的意思就是你对主动对雇工说:“你睡觉去吧,用着你的时候我主动去叫你,然后接着干活”。
睡眠的意思就是你主动对雇工说:“你睡觉去吧,某时某刻过来报到,然后接着干活”。
阻塞的意思就是,你发现你的雇工不知道在什么时候没经过你允许,自己睡觉呢,但是你不能怪雇工,肯定你这个雇主没注意,本来你让雇工扫地,结果扫帚被偷了或被邻居家借去了,你又没让雇工继续干别的活,他就只好睡觉了。雇工一旦发现扫帚回来了,他就会自己去干活的。

 

3:FreeRTOS_delay(portMAX_DELAY):当延时值为MAX时,此延时将转别为挂起函数,即即使真的延时了portMAX_DELAY这么久,任务也不会被唤醒;而如果将延时值设为portMAX_DELAY-1,则真延时portMAX_DELAY-1这么久后,任务会被唤醒

由字母数字下划线组成且以字母和下划线开始的标号都是地址,任何一个标号都涉及地址和其值两个概念, int a = 4,int *b = &a,假设a地址0x20000000,b地址0x20000004,则a的值为4,b的值为0x20000000,a的值和b的值会被分别存到0x08000000和0x08000004中; int a = 4,const int *b = &a,假设a地址0x20000000,b地址0x08000004,则a的值为4,b的值为0x20000000,a的值和b的值会被分别存到0x08000000和0x08000004中;

 

4:同等级下的函数调用会触发上下文切换,因为被调程序也需要使用这些寄存器 ;但这里的上下文切换由编译器自动实现入栈和出栈

中断会触发上下文切换,系统调用是通过软件中断实现的,因此也有上下文切换;

区分压栈和上下文:上下文的定义是cpu内寄存器的值,而压栈的定义是往栈空间内放数据;因此上下文切换肯定需要压栈操作,而反之则未必

当事件或指令导致程序执行中断时,就会发生上下文切换。然后,CPU需要先处理此异常。 导致程序执行中断的事件和指令有:

 •中断或服务请求
 •陷阱
 •函数调用
 
5:mask 面具,遮挡,屏蔽;因此mask一个中断表示关闭/屏蔽/不响应此中断;unmask为使能一个中断,NMI 为不可屏蔽中断即这个中断关闭/屏蔽不了,必然会响应
 
6: Cortex-M3 中发生异常时,会硬件压栈; Cortex-M3 函数调用时是不会硬件压栈的,需要软件压栈。但是,用C语言时编译器会自动生成压栈的汇编语句。若用汇编编写,则需要手写压栈。
当程序跳到中断向量表时就会硬件自动压栈,进入异常后,异常不用LR寄存器(R14)保存返回地址,但是需要使用LR寄存器触发异常返回机制
 
7: cm3架构下函数调用需要进行压栈和出栈,进入子程序前需要保存主调函数的寄存器值,子程序中会为寄存器赋予新值,退出子程序前会出栈还原主调函数调用子程序前的寄存器值,函数体的{ }分别对应压栈和出栈的操作,这些操作会由编译器自动生成汇编代码插入子程序的开头和结尾;即函数调用时是不会硬件压栈的,需要软件压栈。但是,用C语言时编译器会自动生成压栈的汇编语句。若用汇编编写,则需要手写压栈。正常(非异常中断)函数的执行只会使用部分寄存器,而这些寄存器{R0-R3,R12,LR,PC,xPSR}正是函数调用时编译器自动压/出栈的那些寄存器,因此正常的函数调用时上下文由编译器自动处理,且在同一个栈空间内压/出栈;中断打断用户程序时,会在进入isr前压用户的栈,出isr后出用户的栈,操作的也是一个栈,但有任务切换的pendsv中断中会有特殊操作;由于硬件在进pendsv前已经完成了task1用户栈的压栈,因此中断中不需要处理此部分,而只需要将剩下未处理的寄存器{r4-r11, r14}压栈即可, 同时isr中代码会将psp的值由task1的栈顶变为task2的栈顶,同时对task2进行{r4-r11, r14}的出栈,等isr退出后再由硬件完成task2剩下寄存器的自动出栈,因此在pendsv的中断中实现了对task1的压栈和对task2的出栈,同时将psp由中断前的task1栈变更为中断后的task2栈。
主调函数负责{R0-R3,R12,LR,PC,xPSR}的维护,因此在调用子函数的前后需要对这些寄存器做压栈和出栈的操作;默认被调函数不使用{r4-r11, r14},仅使用{R0-R3,R12}即可,因此被调函数中必须使用{r4-r11, r14}时必须在被调函数中先对这些寄存器压栈,之后操作,最后被调函数在退出前需要对它们出栈。
 
8: 如果一个函数需要软件来保护寄存器,那么不管是不是中断,都要软件保护寄存器;如果一个函数不需要软件来保护寄存器,那么不管是不是中断,都不要软件保护寄存器。所有函数间的调用都需要由编译器自动生成汇编代码插入子程序的开头和结尾,所有的调用在编译之后都是确定的,编译之后所有的调用处也都添加了push和pop的汇编指令;而异常触发打断执行流是不确定的,是不需要也没办法在编译时插入push和pop的汇编命令,而是由硬件自动压/出栈实现上下文保护的;而函数退出时是根据LR的值由硬件自动区分是从isr中退出(硬件自动做出栈操作)还是从一般的函数调用中(由编译器生成的pop汇编做出栈操作)退出
 
9:运行态的任务处于就绪链表中

10:os 提供的延时函数一般是非阻塞的延时,直接调用硬件定时器的延时为阻塞延时; 所谓阻塞延时是指这段延时时间内cpu一直轮询延时结果,CPU全程陷入到这里,无法做有效的事情;而 os_delay 被调用后cpu并没有陷入延时内部,而是去执行其他 task 的有效代码

 

11:进入阻塞态的唯一方式是在运行态下延时或获取事件,因此进入阻塞态前的任务处于运行态且是优先级最高的;阻塞态中的任一任务优先级都要比当前处于运行态的任务优先级高(因为只有高优先级的任务离开运行态后才能让当前的任务切换为运行态);因此从事件或延时链表中解除阻塞的任务,其优先级必大于当前运行态的优先级,必然需要一次任务切换

 
12:所谓调度其实就是切换;调度器只调度处于就绪态和运行态的任务,处于阻塞态和挂起态的任务不参与调度;因此vTaskSuspendAll后等同于把所有的就绪态任务都变为挂起态,因此只剩下当前处于运行态的这一个任务,直接变为单任务串行裸机程序流
 
13:调度器挂起期间,所有从阻塞链表因延时到期或获取到事件解除阻塞的任务都会先暂时放到挂起链表中,然后等调度器恢复时将挂起链表上的所有任务都转移到对应优先级的就绪链表中;调度器挂起后只剩一个任务在运行了,谁去管延时是否到期或者释放事件呢?答案是FROM_ISR,即有可能在调度器挂起期间有中断产生
 
14:因获取事件(时间事件或同步事件)而阻塞的任务是放在延时队列里的,因为获取同步事件且超时值为0的任务是不会被阻塞的,因此只有那些获取同步事件且超时值不为0的任务才会被阻塞,因此根据超时值任务要挂在延时队列里;而等超时后不论试图获取的同步事件是否获取到这些任务都会被转移到就绪队列里
 
15:mutex的持有者是指第一个拿到mutex的任务,之后即使其他task试图获取mutex,也不会改变mutex的持有者,只会改变mutex的继承优先级
 
16:如果一个全局的资源或变量会被多个task使用,那么每个task操作此资源时就需要增加临界区实现互斥操作;如果一个全局的资源或变量只会被一个task使用,那么这个task操作此资源时就不需要增加临界区实现互斥操作;
 
17:vListInsertEnd 是将一个item放入到list当前index的前面,而不是放入整个list的结尾处;
vListInsert 会遍历整个链表按照item的value值按照升序排序放置
升序排序头item为: pxList ->xListEnd ->pxNext;
升序排序尾item为: pxList ->xListEnd ->pxPrevious;
pxIndex用于遍历链表指向任一item,而不是固定指向xListEnd
升序排序从左到右项值为:val+1 -> val+2 -> val+3 -> .... -> val+n -> MAXVAL (val+ 表示值越来越大)
升序排序从左到右后项为:item1 -> item2 -> item3 -> .... -> itemn -> xListEnd -> item1 (-> 表示next成员)
升序排序从左到右前项为:item1 <- item2 <- item3 <- .... <- itemn <- xListEnd <- item1 (<- 表示previous成员)
 
18:FreeRTOS中的事件集只使用了24位,同时xWaitForAllBits不是等待24个位全到,而是假设等bit2和3,那么当bit2和3全到时就解除阻塞
 
19:FreeRTOS 中栈指针的类型为uint32*,而没用uint32,理解1:虽然地址是个uint32的数,也可以通过 *(uint32 *)addr来获取地址处的值,但FreeRTOS的栈是使用uint32数组的形式实现,因此遍历数组内的元素值(其实就是cpu中寄存器中的数值,这个数值既可以是地址值也可是数据值)就需要一个数组类型的指针即uint32*,这个uint32*的数据指向(等于)数据内各元素的地址值。 理解2:因为压栈是将一个32bit的数放入某一地址下,因此一个32bit数的地址由uint32*的变量去接。
 
posted on 2023-07-25 21:44  lance9527  阅读(151)  评论(0编辑  收藏  举报