第一个执行的是启动文件里面由汇编编写的复位函数Reset_Handler。
复位函数的最后会调用 C 库函数__main。__main 函数的主要工作是初始化系统的堆和栈
最后调用 C 中的 main 函数,从而进入 C语言程序。
在 main 函数中将硬件初始化,RTOS 系统初始化,所有任务的创建这些都弄好,最后在启动任务调度器
main()中创建任务 xTaskCreate()函数
main()函数中直接初始化我们的硬件外设,然后进行任务的创建即可——xTaskCreate(),在任务创建中,FreeRTOS 会帮我们进行一系列的系统初始化,在创建任务的时候,会帮我们初始化堆内存。
开启任务调度器vTaskStartScheduler()函数
在创建完任务的时候,我们需要开启调度器,因为创建仅仅是把任务添加到系统中,还没真正调度,并且空闲任务也没实现,定时器任务也没实现,这些都是在开启调度函数vTaskStartScheduler()中实现的。
为什么要空闲任务?因为 FreeRTOS 一旦启动,就必须要保证系统中每时每刻都有一个任务处于运行态(Runing),并且空闲任务不可以被挂起与删除。
空闲任务的优先级是最低的,以便系统中其他任务能随时抢占空闲任务的 CPU 使用权。这些都是系统必要的东西,也无需用户自己实现,FreeRTOS 全部帮我们搞定了。处理完这些必要的东西之后,系统才真正开始启动
空闲任务谁创建的?
1.简述一下什么是RTOS系统
FreeRTOS为例,RTOS系统是以抢占优先级为主,时间片为辅实现任务调度的操作系统,主要核心上实现的是任务调度的功能,使任务具有实时性。
2.在任务中怎么进行数据传输?什么方式?
任务间数据传输一共有三种形式,用的最多的是消息队列,其次是全局变量和信号量。
1.消息队列发送数据的方式可以是发送数据本身和发送数据的地址指针。
2.全局变量使用供所有任务获取和处理,但全局变量占用内存较多,而且不好管理。
3.信号量一般作为标志位使用。
3.阐述信号量的作用,信号量的类型
信号量共有三种,二值信号量、计数信号量、互斥信号量,常用的是二值信号量和互斥信号量。
信号量本质上都是深度为1的消息队列。
二值信号量通常在中断中作为标志位使用,起到线程同步的作用。
互斥信号量作为互斥锁使用。防止不同线程访问同个内存。
4.FreeRTOS有多少个优先级?任务优先级和系统优先级是什么关系?
FreeRTOS一共有32个任务优先级,但实际使用的优先级个数通过系统设置使用。
系统优先级NVIC基于RAM架构,优先级最高,是一切中断的基石。任务优先级基于FreeRTOS,和系统优先级无关。
5.如何处理优先级反转问题?
FreeRTOS 之 优先级翻转 - 简书
更新:最近面了某大厂,感觉有工作经验了就不注意基础,结果这次被问懵逼了,错失了机会。
2022.05.19
6.请阐述一下串口发送接收从链路层到应用层的过程。
其实这个问题我都不理解面试官到底想要什么答案,感觉就中断接收后数据放到缓存中,然后从缓存中取数据进行处理,我的理解就是这样,但是面试官并不满意这个答案,所以我一脸懵逼,不知道想问什么,还是自己太菜了吧。
7.请阐述一下网络的层级架构
这个就是OSI7层架构的阐述,毕业就没再看过了,真的时间太久了,7层根本就说不全。。
8.指针数组和数组指针有区别吗?如果有区别的话,区别是什么呢?
这个确实是个好问题,但因为从没看过,还是不会。。
正确答案:
指针数组是指是数组中存的都是指针变量如下图:
数组指针指的是一个指针变量,这个指针指向一个数组。
其他的区别就是sizeof()他们的大小不同,这个Int变量的数组指针的大小是4字节,而指针数组的大小是该数组的变量多少决定的。
还问了之前更新的一些内容,答得还不错就没印象了。剩下的就针对个人项目经历针对的问问题。
这次面试真的是血的教训,无论已经拥有了多少年工作经验,基础都是重中之重,以后再也不没复习就去面试了。。错失了一次比较不错的机会,有些可惜。
RTOS的实时性是如何实现的
一个处理器核心在某一时刻只能运行一个任务,操作系统中任务调度器的责任就是决定在某一时刻究竟运行哪个任务。
实时操作系统中都要包含一个实时任务调度器,这个任务调度器与其它操作系统的最大不同是强调:严格按照优先级来分配CPU时间,并且时间片轮转不是实时调度器的一个必选项。
FreeRTOS就是一款支持多任务运行的实时操作系统,具有时间片、抢占式和合作式三种调度方式。
合作式调度,主要用在资源有限的设备上面,现在已经很少使用了。出于这个原因,后面的FreeRTOS版本中不会将合作式调度删除掉,但也不会再进行升级了。
抢占式调度,每个任务都有不同的优先级,任务会一直运行直到被高优先级任务抢占或者遇到阻塞式的 API 函数,比如 vTaskDelay。
时间片调度,每个任务都有相同的优先级,任务会运行固定的时间片个数或者遇到阻塞式的 API函数,比如vTaskDelay,才会执行同优先级任务之间的任务切换。
抢占式调度器
使用了抢占式调度,最高优先级的任务一旦就绪,总能得到 CPU 的控制权。 比如,当一个运行着的任务被其它高优先级的任务抢占,当前任务的 CPU 使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了 CPU 的控制权并运行。 又比如,如果中断服务程序使一个高优先级的任务进入就绪态,中断完成时,被中断的低优先级任务被挂起,优先级高的那个任务开始运行。使用抢占式调度器,使得最高优先级的任务什么时候可以得到 CPU 的控制权并运行是可知的,同时使得任务级响应时间得以最优化。
总的来说,学习抢占式调度要掌握的最关键一点是:每个任务都被分配了不同的优先级,抢占式调度器会获得就绪列表中优先级最高的任务,并运行这个任务。
在 FreeRTOS 的配置文件 FreeRTOSConfig.h 中禁止使用时间片调度,那么每个任务必须配置不同的优先级。当 FreeRTOS 多任务启动执行后,基本会按照如下的方式去执行:
1.首先执行的最高优先级的任务 Task1,Task1 会一直运行直到遇到系统阻塞式的 API 函数,比如延迟,事件标志等待,信号量等待,Task1 任务会被挂起,也就是释放 CPU 的执行权,让低优先级的任务得到执行。
2.FreeRTOS 操作系统继续执行任务就绪列表中下一个最高优先级的任务 Task2,Task2 执行过程中有两种情况:
Task1由于延迟时间到,接收到信号量消息等方面的原因,使得 Task1从挂起状态恢复到就绪态,在抢占式调度器的作用下,Task2的执行会被 Task1 抢占。
Task2 会一直运行直到遇到系统阻塞式的 API函数,比如延迟,事件标志等待,信号量等待,Task2任务会被挂起,继而执行就绪列表中下 一个最高优先级的任务。
3.如果用户创建了多个任务并且采用抢占式调度器的话,基本都是按照上面两条来执行。 根据抢占式调度器,当前的任务要么被高优先级任务抢占,要么通过调用阻塞式 API 来释放 CPU 使用权让低优先级任务执行,没有用户任务执行时就执行空闲任务。
任务之间是如何通信的
在FreeRTOS中所有的通信和同步机制都是基于队列(FIFO)实现的。但是也可以使用 LIFO 的存储缓冲,也就是后进先出,FreeRTOS 中的队列也提供了 LIFO 的存储缓冲机制
1.什么是队列
队列是数据结构中的一个概念,其最大的特性为先进先出(FIFO:First In First Out)
2. 队列特点:
1、多任务访问
队列不是属于某个特别指定的任务的,任何任务都可以向队列中发送消息,或者从队列中提取消息。
2、出队阻塞
当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任务从队列中读取消息无效的时候任务阻塞的时间。
3、入队阻塞
入队说的是向队列中发送消息,将消息加入到队列中。
3. 队列创建
在使用队列之前必须先创建队列,有两种创建队列的方法,一种是静态的,使用函数xQueueCreateStatic();另一个是动态的,使用函数 xQueueCreate()
1、函数 xQueueCreate() 此函数本质上是一个宏,用来动态创建队列
2、函数 xQueueCreateStatic() 此函数也是用于创建队列的,但是使用的静态方法创建队列,队列所需要的内存由用户自行分配
3、函数 xQueueGenericCreate() 函数 ,用于动态创建队列,创建队列过程中需要的内存均通过FreeRTOS 中的动态内存管理函数 pvPortMalloc()分配
4. 队列初始化函数
队列初始化函数 prvInitialiseNewQueue()用于队列的初始化
5. 向队列发送消息
6. 从队列读取消息
二值信号量和互斥量的区别
信号量一般用来进行资源管理和任务同步,FreeRTOS中信号量又分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量,不同的信号量其应用场景不同。
1. 信号量简介
信号量常常用于控制对共享资源的访问和任务同步
2. 二值信号量
二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问,
和队列一样,信号量 API 函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数
二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的,这不正好就是二值的吗? 任务和中断使用这个特殊队列不用在乎队列中存的是什么消息,只需要知道这个队列是满的还是空的。可以利用这个机制来完成任务与中断之间的同步。
3. 互斥信号量
互斥信号量其实就是一个拥有优先级继承的二值信号量,互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。
当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低。
● 互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
● 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
4.计数型信号量
同二值信号量一样,用户不需要关心队列中存储了什么数据,只需要关心队列是否为空即可。计数型信号量通常用于如下两个场合:
1、事件计数
在这个场合中,每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值),其他任务会获取信号量
2、资源管理
在这个场合中,信号量值代表当前资源的可用数量,比如停车场当前剩余的停车位数量。一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量,释放信号量以后信号量值会加一。在这个场合中创建的计数型信号量初始值应该是资源的数量,比如停车场一共有 100 个停车位,那么创建信号量的时候信号量值就应该初始化为 100。
任务通知是怎么是实现的
1. 任务通知简介
任务通知在 FreeRTOS 中是一个可选的功能,要使用任务通知的话就需要将宏 configUSE_TASK_NOTIFICATIONS 定义为 1。 FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue 就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任 务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:
● 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理)。
● 覆盖接收任务的通知值。
● 更新接收任务通知值的一个或多个 bit。
● 增加接收任务的通知值。
RTOS内核是怎么调度的
FreeRTOS四种任务状态
FreeRTOS的任务状态(4种)
1.运行态(Running) 2.就绪态(Ready) 3.阻塞态(Blocked) 4.挂起态(Suspended)
Running—运行态
当任务处于实际运行状态被称之为运行态,即 CPU 的使用权被这个任务占用。
Ready—就绪态
处于就绪态的任务是指那些能够运行(没有被阻塞和挂起),但是当前没有运行的任务,因为同优先级或更高优先级的任务正在运行。
Blocked—阻塞态
由于等待信号量,消息队列,事件标志组等而处于的状态被称之为阻塞态,另外任务调用延迟函数也会处于阻塞态。
Suspended—挂起态
类似阻塞态,通过调用函数 vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行,只有调用函数 xTaskResume()才可以将这个任务从挂起态恢复。