AliOS Things是一款支持单处理器上运行多个任务的实时操作系统。操作系统内核只包含用来控制系统资源和处理器对资源的使用的基础功能,来支持系统服务和上层应用的构建和开发。 AliOS Things操作系统内核特性如下:
Apache license v2.0
任务可以认为是一段独享CPU的运行程序,而应用是完成特定功能的多个任务的集合。任务管理就是为多任务环境中的每个任务分配一个上下文(context)(上下文(context)是指当任务被调度执行的所必不可少的一组数据,包括前任务的CPU指令地址(PC指针),当前任务的栈空间,当前任务的CPU寄存器状态等),在任务相继执行过程中,将切出任务的信息保存在任务上下文中,将切入任务的上下文信息恢复,使其得以执行。为维护任务上下文、状态、栈等相关信息,操作系统内核为每个任务定义了一组数据结构,即任务控制块(Task Control Block),来存放这些信息。 任务调度负责将处理器资源分配给关键任务,让其优先运行。所以系统中的每个任务需要根据关键程度分配不同的优先级,那些执行关键操作的任务被赋予高优先级,而一些非关键性任务赋予低优先级。当系统发生调度时,高优先级任务可以抢占低优先级任务的处理器资源得到调度执行。系统在无任务可调度时,就运行空闲任务,其优先级最低。 任务被创建时,需要为任务指定执行体入口地址、栈大小、优先级等信息,创建过程中内核为任务分配任务控制块(TCB)。任务栈空间可以在任务创建时由用户程序指定,也可以由内核根据用户指定大小来动态分配。操作系统内核提供基于软件的栈溢出检测方法,用户可根据需要配置或关闭该功能。
任务状态是反映当前系统中任务所处的状况,操作系统内核需要维护所有任务的当前状态。AliOS Things为了充分描述任务在系统中所处的状态以及引发状态迁移的条件差异,将任务状态分为就绪状态、挂起状态、休眠状态、阻塞状态、运行状态和删除状态。当任务通过aos_task_create()创建时,任务处于挂起状态,当任务通过aos_task_del()删除时,任务处于删除状态,具体的转化过程如下图:
(1)阻塞状态是指因等待资源而处于等待状态,如调用aos_mutex_lock()获取互斥量时互斥量已经被锁定、调用aos_queue_recv()获取队列数据时队列为空、调用aos_sem_wait()等待信号量时信号量计数为0、调用aos_evnet_get()获取事件时,事件还未发生; (2)挂起状态是因任务被其他或自身调用挂起函数aos_task_suspend()后,将无条件地停止运行,被挂起的任务只能通过其他任务调用恢复函数aos_task_resume()使其恢复到就绪状态; (3)休眠状态是因任务在调用任务休眠函数aos_msleep()后,进入一种延迟执行的状态,直到休眠时间到达任务被重新调度恢复运行。 (4)删除状态是因任务运行完成调用任务退出函数aos_task_exit()或被调用任务删除函数aos_task_del()时被设置的一种状态。 (5)就绪状态是在任务被创建或任务解除阻塞或延迟到期时,任务被置为就绪状态。只有当任务处于就绪状态时,才能被系统调度进入运行状态。 (6)运行状态是获取处理器执行权的就绪任务所处的状态,单处理器系统,任意时刻只有一个任务可以运行。 AliOS Things允许任务处于组合状态,如阻塞挂起状态:任务在阻塞状态下,被其他任务挂起,则进入阻塞挂起状态。该状态下,若任务被恢复则保持阻塞状态;若任务解除阻塞则保持挂起状态。 用户可通过tasklist命令查看任务状态,任务状态描述符号和含义如下表:
状态符号 | 描述 |
---|---|
RDY | 任务已在就绪队列或已被调度运行,处于就绪状态或运行状态 |
PEND | 任务因等待资源或事件的发生而处于阻塞状态 |
SUS | 任务因被其他或自身调用挂起函数aos_task_suspend()后所处的挂起状态 |
SLP | 任务处于休眠状态 |
PEND_SUS | 任务在阻塞状态下,被其他任务挂起,处于阻塞挂起状态 |
SLP_SUS | 任务在休眠状态下,被其他任务挂起,处于休眠挂起状态 |
DELETED | 任务处于删除状态 |
任务调度是为多任务环境中的就绪任务分配处理器资源。AliOS Things操作系统内核支持两种调度策略:
该调度策略下,每个任务优先级都维护了一个FIFO模式的就绪队列(ready queue),里面包含了当前所有可运行的任务列表,此列表中的任务都处于就绪状态,当处理器可用时,最高优先级的就绪队列的第一个任务得到处理器被执行。当有一个高优先级任务进入就绪队列,正在运行的低优先级任务会立即被唤出,将处理器执行权交给高优先级任务。此种调度机制存在一个潜在问题,如果存在多个优先级相同的任务,其中一个任务强占处理器,则其他同等优先级的任务将无法被执行。时间片轮转调度可以避免这个问题。
时间片轮转调度使用时间片控制每个任务的执行时间,同等优先级的任务依次获得处理器被调度执行,每个任务可以运行的时间片是固定的,当任务的时间片用完后,该任务被放在对应优先级就绪队列的队尾,然后调度就绪队列第一个位置上的任务来执行。
AliOS Things操作系统内核允许的任务优先级分配范围默认为0~62,值越大表示优先级越低,其中空闲任务的优先级是61,即系统最低优先级。
任务栈保护采用栈统计的方法在任务切换时,对当前任务栈的使用情况进行统计,当检测到任务栈使用量超过了预先分配的空间,则抛出异常。
空闲任务是一个无限循环函数,在进入循环前或循环体内允许用户配置钩子函数。
AliOS Things操作系统内核提供了任务创建、任务删除、任务延迟、获取任务名称等任务相关的服务函数,提供给应用程序调用,具体描述如下:
函数名 | 描述 |
---|---|
aos_task_create() | 任务创建函数(推荐) |
aos_task_new() | 任务创建函数(兼容3.1) |
aos_task_new_ext() | 任务创建函数(兼容3.1) |
aos_task_exit() | 任务退出函数 |
aos_task_delete() | 任务删除函数 |
aos_task_resume() | 任务恢复函数 |
aos_task_suspend() | 任务挂起函数 |
aos_task_yield() | 任务让出CPU函数 |
aos_task_self() | 获取当前任务的具柄 |
aos_task_name_get() | 获取任务名称 |
任务优先级最大值: 默认62, 最大不能超过256,如需修改,在yaml中修改RHINO_CONFIG_PRI_MAX配置
任务栈溢出检测: 默认关闭, 如需修改,在yaml中修改RHINO_CONFIG_TASK_STACK_OVF_CHECK配置
空闲任务栈大小: 默认100Bytes, 如需修改,在yaml中修改RHINO_CONFIG_IDLE_TASK_STACK_SIZE配置
时间片轮转调度策略: 默认使能, 如需修改,在yaml中修改RHINO_CONFIG_SCHED_RR配置
完全公平调度策略: 默认关闭, 如需修改,在yaml中修改RHINO_CONFIG_SCHED_CFS配置
示例代码参考example/task_example.c,该示例使用任务管理函数来控制任务的执行状态,具体场景为任务2因等待某个信号量进入阻塞状态,而此时被任务1将其挂起,则任务2仍然是处于阻塞状态,如果在此过程中等到信号量,则任务2会解除阻塞进入挂起状态;如果未等到信号量,则任务2恢复状态后仍然处于阻塞状态。 示例说明如下:
该示例可配置到helloworld_demo案例中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具 ,所以首先需要参考《AliOS Things集成开发环境使用说明之搭建开发环境》,下载安装 。 待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
案例下载完成后,需要在helloworld_demo的package.yaml中添加
在已安装了 的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
在osal_aos组件的package.yaml中添加example示例代码:
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
CLI命令行输入:
关键日志:
答:任务栈必须满足应用程序对函数嵌套调用所需的空间,用户可以通过tasklist查看栈的使用情况,根据需 要调整栈大小,这样可以在防止栈溢出的同时,也可避免栈空间分配过大造成浪费。
答:aos_task_yield()仅仅是让出CPU,它仍然处于就绪状态,因为该操作只是将任务放置到就绪队列的队 尾,若无更高优先级任务在就绪队列中,它就可以被调度执行。而aos_task_suspend()是将任务挂起停 止运行,任务会从就绪队列中去除,只有当任务被其他任务调用aos_task_resume()才能恢复。
AliOS Things操作系统内核使用tick作为时间片轮转调度以及延迟操作的时间度量单位,tick是实现定时触发功能的基础。tick计数发生在每次时钟中断处理的过程,时钟中断是定时产生的,系统在默认情况下为1ms触发一次,即一个tick代表1ms,用户可根据应用需要调整该时间。 软件定时器是用来在指定时间或者触发一次或多次某个功能函数的调用。这种由定时器来执行的函数叫做定时器回调函数,定时器回调函数以及触发时间由应用程序来设定。当定时器的触发时间到来,则定时器的回调函数会被执行。定时器支持两种工作模式:单次模式和周期性模式。定时器在创建时,用户可通过option参数来制定该定时器的工作模式是单次还是周期性的,若option设置了AOS_TIMER_REPEAT则为周期性的,否则为单次。
定时器会从应用程序设置的初始时间开始,以tick为计时单位进行倒计时,当计数值减为0时调用回调函数执行。回调函数执行完毕,则定时器停止。
周期定时器在时间到期执行完回调函数后,重新开始计时,直到下次时间到期,再次执行回调函数,然后一直循环下去。
为了在定时器创建、删除、启动、停止以及参数并更的过程中,避免锁操作,系统采用一个定时器任务来处理这些操作。当应用程序调用定时器管理接口时,将接口调用命令发送到一个消息队列,定时器任务从消息队列中读取消息并处理该命令。 定时器任务除了处理命令外,还对当前已运行的定时器进行实时调度。所有正在运行的定时器会被挂接到g_timer_head链表,定时器任务循环的从g_timer_head链表中取出时间最近一次的定时器,通过当前tick计数和该定时器的超时tick数来判断定时时间是否到,如果该定时器触发时间已到则立即执行其处理函数,否则代表最近一次的定时器时间尚未到来,则在此时差,继续从命令buffer中收取消息,直到时间差到来后,再立即执行定时器处理函数。 上述流程在g_timer_head中有待处理定时器时才会进入,如果没有待处理定时器,在定时器任务将只会进入定时器管理循环中。
函数名 | 描述 |
---|---|
aos_timer_create() | 定时器创建函数(推荐) |
aos_timer_new() | 定时器创建函数(兼容3.1) |
aos_timer_new_ext() | 定时器创建函数 |
aos_timer_free() | 定时器删除函数 |
aos_timer_start() | 定时器启动函数 |
aos_timer_stop() | 定时器停止函数 |
aos_timer_change() | 定时器初始时长和周期间隔参数变更函数 |
aos_timer_change_once() | 定时器初始时长参数变更函数 |
aos_timer_gettime() | 获取定时器时间参数函数 |
软件定时器功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_TIMER配置
软件定时器任务栈: 默认200Bytes,如需修改,在yaml中修改RHINO_CONFIG_TIMER_TASK_STACK_SIZE配置
软件定时器任务优先级: 默认5,如需修改,在yaml中修改RHINO_CONFIG_TIMER_TASK_PRI配置
定时器消息队列消息数: 默认20,如需修改,在yaml中修改RHINO_CONFIG_TIMER_MSG_NUM配置
每秒tick数: 默认100,如需修改,在yaml中修改RHINO_CONFIG_TICKS_PER_SECOND配置
时间片: 默认50个tick,如需修改,在yaml中修改RHINO_CONFIG_TIME_SLICE_DEFAULT配置
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
helloworld_demo组件的package.yaml中添加
在已安装了 的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
osal_aos组件的package.yaml中添加example示例代码:
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
CLI命令行输入:
关键日志:
答:定时器回调函数是在定时器任务中执行的,所以定时器回调函数的执行上下文为定时器任务,当回调函 数所需栈空间超过定时器任务栈大小时,用户需要通过配置 RHINO_CONFIG_TIMER_TASK_STACK_SIZE参数来增加栈大小。
AliOS Things操作系统内核通过内存管理实现应用程序可以调用标准C库的malloc和free接口来动态的分配和释放内存。
函数名 | 描述 |
---|---|
aos_malloc() | 从系统heap分配内存给用户 |
aos_zalloc() | 从系统heap分配内存给用户,并且将分配的内存初始化为0 |
aos_calloc() | 从系统heap分配内存给用户,并且将分配的内存初始化为0 |
aos_realloc() | 重新调整之前调用 aos_malloc(aos_calloc、aos_zalloc) 所分配的内存块的大小 |
aos_free() | 内存释放函数 |
AliOS Things内存管理采用类buddy伙伴算法,以及blk快速小内存申请算法相结合的策略。
Buddy算法申请的内存最小为8字节对齐 可申请的最大长度除了受堆空间总大小限制外还可通过编译宏配置 通过32 bit的位图结构来挂载对应的空闲链表bit n代表内存块对应的type,其下面挂载了长度范围 (2^(n- 1 + off) ~ 2^(n + off) - 1)的空闲块,其中off值主要是考虑利用了原本较小长度块的低bit。 内存申请时,优先从对应type的空闲链表中申请;如果没有空闲,则从 type+1 的位置申请内存,并将多余的空闲内存块做拆分并重新放入对应的空闲链表位置;每个内存块维测信息,包括如魔术字、多级申请调用栈、是否空闲等信息,便于内存踩踏、泄漏、重复释放等问题的定位。 内存释放时,考虑前后的内存块是否也空闲,如果是,则进行内存块合并,并插入相应type的空闲链表位置。 优点: 能够满足绝大多数场景的内存申请需求; 可以申请最大范围内任意长度的内存; 内存碎片整理已经相当出色; 有充分的维测信息,便于问题定位。 缺点: 内存维测信息较多,有一定的空间浪费; 内存不停地拆分组合,一定程度上影响效率; 长期运行过多的小内存会出现碎片,会隔断大内存空间。
blk小内存算法申请的内存最小为4字节对齐 小于RHINO_CONFIG_MM_BLK_SIZE大小的内存块会优先从blk算法中申请不够的再从通过buddy申请 内存申请时,优先从对应type的空闲链表中申请;如果没有空闲,则先考虑从空闲资源池拉取一段空闲资源补充;如果空闲资源不足,则从 type+1 的位置申请内存(以此类推)申请过程不涉及拆分; 内存释放时,直接挂在到对应type的空闲链表内,释放不涉及合并。 优点: 申请效率高; 避免过多小内存碎片。 缺点: 内存维测信息少,出现问题较难定位; 申请的内存大小有限制,一般为1K以下。
内存的常用配置都在各个单板的k_config.h中,如果不需修改,则会使用k_default_config.h里面的默认配置。当然由于其本身是宏定义,也可在board对应的yaml中定义。
打开关闭内存模块功能
打开关闭buddy内存模块的维测功能
内存维测打开时,记录内存申请时的多少级调用栈
Buddy算法最大支持申请的内存大小bit位 实际大小换算为 1ul << RHINO_CONFIG_MM_MAXMSIZEBIT
打开关闭固定长度小内存块快速申请
固定长度小内存块总空间大小(byte)
设定从blk小内存申请的阈值(byte)即
注: blk目前最大支持1K以下的小内存块如果需要调整可修改内部配置 MM_BLK_SLICE_BIT一般无需修改
该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具 ,所以首先需要参考《AliOS Things集成开发环境使用说明之搭建开发环境》,下载安装 。 待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
helloworld_demo组件的package.yaml中添加
在已安装了 的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
osal_aos组件的package.yaml中添加example示例代码:
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
CLI命令行输入:
关键日志:
答:通过打开宏RHINO_CONFIG_MM_DEBUG即可打开内存管理模块的维测信息;其包含内存头魔术字、内存申请的调用栈以及当前内存空闲情况等等。 其中blk算法不包含维测信息。
答:通过在cli中输入 dumpsys_mm_info ,将所有内存块的使用情况,以及整体的占用空闲情况输出。
信号量是多任务系统中实现任务间同步,并且协调多任务对共享资源访问的一种互斥机制。信号量允许有多个使用者,所以采用计数值来表示可用的资源数,当请求一个信号量时,该计数值减1,若此时计数值大于等于0,表示当前有可用的信号量,则任务获得信号量,可以访问资源,若此时计数值为负数,则任务进入阻塞状态,释放处理器资源。当获取信号量的任务执行完操作,释放信号量时,则将当前计数值加1,如果当前存在等待该资源的任务,则任务被唤醒而获得该信号量。
信号量获取可设置超时时间,如果任务在超时时间到期后仍未等待信号量,则任务解除阻塞进入就绪状态。
多个任务可以等待同一个信号量,若信号量可用或信号量被释放,通常情况下,系统会将阻塞在该信号量上优先级最高的任务置于就绪状态,提供了面向所有阻塞任务的信号量释放机制,可以将阻塞在该信号量上的所有任务都置为就绪状态。
函数名 | 描述 |
---|---|
aos_sem_create() | 信号量创建函数(推荐),需指定计数值 |
aos_sem_new() | 信号量创建函数(兼容3.1),需指定计数值 |
aos_sem_free() | 信号量删除函数 |
aos_sem_wait() | 信号量获取函数,可以指定超时时间 |
aos_sem_signal() | 信号量释放函数,只唤醒阻塞在此信号量上的最高优先级任务 |
aos_sem_signal_all() | 信号量释放函数,唤醒阻塞在此信号量上的所有任务 |
aos_sem_is_valid() | 判断信号量具柄是否合法函数 |
软件定时器功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_SEM配置
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
在已安装了 的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
在osal_aos组件的package.yaml中添加example示例代码:
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
CLI命令行输入:
关键日志:
信号量的获取接口在中断上下文调用很容易发生死锁问题。当被打断的上下文和打断的中断上下文要获取同一个信号量时,会发生互相等待的情况。有些内核将这种判断处理交由上层软件进行判断和使用,本内核会在take信号量检测,如果是中断上下文,则直接返回失败。
应用程序在获取信号量时,需要按照实际的需求,来安排信号量获取策略。aos_sem_take传入延时timeout为0,获取不到信号量会立即报失败;timeout为AOS_WAIT_FOREVER时,会永远在此等待,直到获取到信号量,可能会造成该任务无法继续运行;其他值标识最大延迟的时间上限,达到上限时,即使未获取到信号量,任务也会被唤醒,并返回错误。
Q1: 调用aos_sem_task()接口无限期的等待信号量,timeout参数怎么设置?
答:将timeout赋值为AOS_WAIT_FOREVER。
互斥量的获取是完全互斥的,即同一时刻,互斥量只能被一个任务获取。而信号量按照起始的计数值的配置,可以存在多个任务获取同一信号量的情况,直到计数值减为0,则后续任务无法再获取信号量,当信号量的计数初值设置为1,同样有互斥的效果。但信号量无法避免优先级反转问题。
优先级反转是一种不希望发生的任务调度状态,该状态下,一个高优先级任务间接的被一个低优先级任务所抢占,使得两个任务的相对优先级反转了。当高、中、低三个优先级任务同时访问使用信号量互斥资源时,可能会引起优先级反转。当高优先级的任务需要的信号量被低优先级任务占用时,处理器资源会调度给低优先级任务。此时如果低优先级需要获取的另一个信号量被中优先级的pend任务所占用,那么低优先级的任务则需要等待中优先级的任务事件到来,并释放信号量,则就出现了 高、中优先级的任务并不是等待一个信号量,但是中优先级任务先运行的现象。如下图:
任务H具有高优先级,任务M具有中等优先级,任务L具有低优先级,在t0时刻任务H还未运行,任务M因等待信号量2而阻塞,低优先级任务L得到调度占用信号量1;t1时刻,任务H运行并抢占任务L,任务L占用信号量还未释放;t2时刻,任务H阻塞于信号量1,任务L得以继续运行;t3时刻,任务L等待信号量2而阻塞,t4时刻,信号量2被释放,任务M得到运行,此时任务H虽然具有高优先级,但需先等待任务M释放信号量2让任务L运行,并且任务L释放信号量1后才能运行,这种情况即出现了优先级反转。
互斥量可以解决优先级反转问题,高优先级的任务获取互斥量时,如果该互斥量被某低优先级的任务占用, 会动态提升该低优先级任务的优先级等于高优先级,并且将该优先级值依次传递给该低优先级任务依赖的互斥量关联的任务,以此递归下去。当某任务释放互斥量时,会查找该任务的基础优先级,以及获取到的互斥量所阻塞的最高优先级的任务的优先级,取两者中高的优先级来重新设定此任务的优先级。总的原则就是,高优先级任务被互斥量阻塞时,会将占用该互斥量的低优先级任务临时提高;互斥量释放时,相应任务的优先级需要恢复。如下图:
任务H具有高优先级,任务M具有中等优先级,任务L具有低优先级,在t0时刻任务H还未运行,任务M获取互斥量2而阻塞,低优先级任务L得到调度获得互斥量1;t1时刻taskH抢占taskL,但因无法获得互斥量1而阻塞,此时taskL的优先级被提升至与taskH一样高,并继续运行;t2时刻taskL因无法获得互斥量2而阻塞,t3时刻互斥量2被释放,taskL因比taskM优先级高获得互斥量2得到运行;在t4时刻,taskL释放互斥量1,并将优先级恢复到之前状态,taskH因获得互斥量1得到运行,该机制消除了优先级反转的发生。
互斥量获取可设置超时时间,如果任务在超时时间到期后仍未获得互斥量,则任务解除阻塞进入就绪状态。
函数名 | 描述 |
---|---|
aos_mutex_create() | 互斥量创建函数(推荐) |
aos_mutex_new() | 互斥量创建函数(兼容3.1) |
aos_mutex_free() | 互斥量删除函数 |
aos_mutex_lock() | 互斥量获取函数 |
aos_mutex_unlock() | 互斥量释放函数 |
aos_mutex_is_valid() | 判断互斥量具柄是否合法函数 |
互斥量优先级继承: 默认关闭,如需修改,在yaml中修改RHINO_CONFIG_MUTEX_INHERIT配置
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
在已安装了 的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
osal_aos组件的package.yaml中添加example示例代码:
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
CLI命令行输入:
关键日志:
Q1: 调用aos_mutex_lock()接口无限期的获取互斥量,timeout参数怎么设置? 答:将timeout赋值为AOS_WAIT_FOREVER。
事件是AliOS Things内核提供的一种任务间通信方式,它不同于信号量和互斥量,可以使用事件组实现一个任务同时等待多个事件的发生,或者等待同一个事件的任务在事件发生时解除阻塞状态。事件组是一个32位的数,每一位都对应一个事件标志,事件标志只有两种状态:
应用程序可以使用事件的如下特性来实现任务间同步:
事件等待可设置超时时间,如果任务在超时时间到期后仍未等到事件发生,则任务解除阻塞进入就绪状态。
函数名 | 描述 |
---|---|
aos_event_create() | 事件创建函数(推荐) |
aos_event_new() | 事件创建函数(兼容3.1) |
aos_event_free() | 事件删除函数 |
aos_event_get() | 获取事件函数 |
aos_event_set() | 设置事件函数 |
aos_event_is_valid() | 判断事件具柄是否合法函数 |
事件功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_EVENT_FLAG配置
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
在已安装了 的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
在osal_aos组件的package.yaml中添加example示例代码:
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
CLI命令行输入:
关键日志:
Q1: 调用aos_event_get()接口无限期的等待事件,timeout参数怎么设置? 答:将timeout赋值为AOS_WAIT_FOREVER。
消息队列是一种任务间传递数据的有效方式。消息队列使用环形缓冲池(ring buffer)来管理消息的队列缓冲区,并使用类似信号量的机制进行任务间的同步。任务通过消息队列可以发送消息,也可以通过它接收消息,从而实现数据的同步及通信。任务发送的消息会暂存在消息队列中,当接收任务来读时,将暂存的数据传递给接收任务;若接收任务在接收数据时,消息队列中无可读数据,则任务会阻塞,直到有消息到来解除阻塞而进入就绪状态。
消息接收可设置超时时间,如果任务在超时时间到期后仍未收到消息,则任务解除阻塞进入就绪状态。
函数名 | 描述 |
---|---|
aos_queue_create() | 消息队列创建函数(推荐) |
aos_queue_new() | 消息队列创建函数(兼容3.1) |
aos_queue_free() | 消息队列删除函数 |
aos_queue_send() | 向消息队列发送消息函数 |
aos_queue_recv() | 从消息队列读取消息函数 |
aos_queue_is_valid() | 判断消息队列具柄是否合法 |
aos_queue_buf_ptr() | 获取消息队列消息数据区地址 |
aos_queue_get_count() | 获取消息队列当前消息数 |
消息队列功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_BUF_QUEUE配置
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
在已安装了 的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
osal_aos组件的package.yaml中添加example示例代码:
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
CLI命令行输入:
关键日志:
Q1: 调用aos_queue_recv()接口无限期的等待消息,ms参数怎么设置? 答:将ms赋值为AOS_WAIT_FOREVER。
在操作系统中,如果我们需要进行一项工作处理,往往需要创建一个任务来加入内核的调度队列。一个任务对应一个处理函数,如果要进行不同的事务处理,则需要创建多个不同的任务。任务作为CPU调度的基本单元,任务数量越大,则调度成本越高。工作队列(workqueue)机制简化了基础的任务创建和处理机制,一个workqueue对应一个实体task任务处理,workqueue下面可以挂接多个work实体,每一个work实体都能对应不同的处理接口。即用户只需要创建一个workqueue,则可以完成多个挂接不同处理函数的工作队列。其次,当某些实时性要求较高的任务中,需要进行较繁重钩子处理时,可以将其处理函数挂在workqueue中,其执行过程将位于workqueue的上下文,而不会占用原有任务的处理资源。另外,workqueue还提供了work的延时处理机制,用户可以选择立即执行或是延时处理。当应用需要创建大量实时性要求不高的任务时,可以使用workqueue来统一调度;或者将任务中实时性要求不高的部分处理延后到workqueue中处理。如果需要设置延后处理,则需要使用work机制。注意:该机制不支持周期work的处理。
workqueue的处理依赖于task任务。一个workqueue队列会创建关联其对应的task任务,一个workqueue会挂载多个work处理,每个work处理对应一个处理函数。当workqueue得到调度,即其关联的task得到运行,在每次task的调度期间,都会从工作队列中按照先后顺序取出一个work来进行处理。workqueue模块在初始化时,会创建一个系统默认的工作队列,用户可根据需要将work添加到该队列中去执行。
函数名 | 描述 |
---|---|
aos_workqueue_create() | 工作队列创建函数 |
aos_workqueue_del() | 工作队列删除函数 |
aos_work_init() | work创建函数 |
aos_work_destroy() | work销毁函数 |
aos_work_run() | work触发函数 |
aos_work_sched() | work触发函数(由系统默认工作队列来执行) |
aos_work_cancel() | 工作队列删除函数 |
消息队列功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_BUF_QUEUE配置
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
在已安装了 的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
osal_aos组件的package.yaml中添加example示例代码:
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
CLI命令行输入:
关键日志:
Q1: 系统默认工作队列对应的任务优先级和栈大小是多少? 答:优先级为:20,栈大小为:3072字节。