高薪秘诀,跟着AliOS Things轻松入门操作系统:任务

简介: 高薪秘诀!

1、任务状态及转换
AliOS Things是一款支持单处理器上运行多个任务的实时操作系统。对于多任务系统,在单个处理器上任意时刻只能有一个任务在运行,其他任务均处于非运行状态。所以这里可以简单的认为任务有两种状态:运行和非运行,如图1。

image.png
图1 任务状态和转换的顶层模型

当任务处于运行状态时,处理器被用来执行该任务的代码;当任务处于非运行状态时,其运行上下文被保存,并在下一次进入运行状态时被恢复执行。当任务恢复执行时,它会从上一次离开运行状态之前要执行的指令开始执行。那任务什么时候又是为什么发生非运行状态与运行状态之间的切换,主要依据系统的调度策略、可引起调度的系统事件以及特定的函数调用。

实时操作系统任务调度依赖于任务优先级,对于那些实时性要求比较高的任务被赋予高的优先级,一旦高优先级的任务准备就绪,系统就会将正在运行的低优先级任务换出,然后将处理器的执行权交给高优先级任务。而对于那些因为某种原因暂时不需要执行而处于非运行状态的任务,只有等到特定事件的发生才会被再次调度执行。所以可以将任务的非运行状态划分为两个子状态:就绪和挂起,如图2。

image.png

图2 任务状态和转换的基础模型

如图2所示,只有当任务处于就绪状态时,才能被系统调度进入运行状态;而处于运行状态的任务可以通过两种方式退出运行:挂起或被抢占。当任务执行完成或者因为某种原因不能继续运行(如等待一定时间段的延迟)则进入一种暂时停止运行的状态即挂起状态。当任务进入挂起状态,它释放处理器资源给其他任务。任务可以因为完成相应操作或自身程序产生信号来主动放弃处理器;任务也会因其他任务抢占、时间到期或系统事件被系统强制换出而被动的放弃处理器资源,任务放弃处理器资源的原因可以参见图3。

image.png
图3 任务挂起的原因

注意,对于工作还未完成被其他任务抢占被强行放弃处理器的任务,不会被挂起而是处于就绪状态,等待被系统再次调度。

结合上述任务挂起的原因分析可以得出任务状态的通用模型如图4所示,其中任务从挂起状态进入就绪状态的条件,包括事件、时间间隔到期、事件和时间间隔到期的组合。
image.png
图4 任务状态和转换的通用模型

2、实现
任务状态是反映当前系统中任务所处的状况,操作系统内核需要维护所有任务的当前状态。由于引发任务状态切换的条件和因素较多,所以不同的操作系统在定义系统任务状态的种类和迁移变化上是有差异的,如图5、图6、图7分别是FreeRTOS、RT-Thread、AliOS Things的任务状态图,它们是在任务状态的通用模型上对挂起状态进行了细化,但细化子类定义是不同的,并且由同类事件引发的任务状态迁移的变化也是有差异的。

2.1、FreeRTOS
FreeRTOS将通用模型中的挂起状态细化为两种状态:阻塞和挂起。任务正在等待事件的状态称为阻塞状态。任务进入阻塞状态等待的事件类型有两种:

(1) 时间相关的事件

延迟间隔的到期或绝对时间的到达。如任务通过调用vTaskDelay()延迟10ms再允许被调度。

(2) 同步事件

其他任务产生的事件或中断。如任务调用xQueueReceive()等待数据到达队列。

处于挂起状态的任务将不再被调度,任务只有被其他任务解除挂起状态才会被调度。处于就绪状态、运行状态或者阻塞状态的任务都可以通过调用vTaskSuspend()进入挂起状态,当有任务调用vTaskResume()将其唤醒时,任务进入就绪状态。

对于新创建的任务因为无需等待任何资源而处于就绪状态,当系统发生调度时,若无更高优先级任务处于就绪状态,则该任务将会被调度进入运行状态。
image.png
图5 FreeRTOS任务状态和转换

2.2、RT-Thread
RT-Thread是在任务状态基础模型上引入了两个新的状态:初始状态和关闭状态。任务在创建完成后进入初始状态,初始状态的任务可以通过调用rt_thread_startup() 函数进入到就绪状态。当任务运行结束执行rt_thread_exit()函数时或被其他任务调用rt_thread_delete/detach() 函数将其删除或销毁时,任务进入关闭状态。处于运行状态的任务可以通过调用 rt_thread_delay(),rt_sem_take(),rt_mutex_take(),rt_mb_recv() 等函数,进入到挂起状态;处于挂起状态的线程,如果等待超时依然未能获得资源或由于其他线程释放了资源,那么它将返回到就绪状态。
image.png
图6 RT-Thread 任务状态和转换

2.3、AliOS Things
AliOS Things为了充分描述任务在系统中所处的状态以及引发状态迁移的条件差异,在上述任务状态的基础模型上新增任务删除状态并对挂起状态进行了细化,具体分为挂起状态、休眠状态和阻塞状态,如图7。
image.png

图7 AliOS Things任务状态和转换

(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()时被设置的一种状态。

与RT-Thread和FreeRTOS相比,AliOS Things对新创建任务的状态给予更多的灵活性。当任务通过调用aos_task_new()函数被创建时,任务状态为就绪状态。若应用程序不要求任务在创建后立即被调度执行,可以通过调用aos_task_new_ext()函数并设置参数autorun为0来创建任务,这时任务处于挂起状态,当应用程序需要任务执行时,可以通过调用aos_task_resume()函数将任务切换至就绪状态。处于就绪状态的任务,在系统发生调度时,优先级最高的任务将会获得处理器的控制权,进入运行状态。若此时有其他更高优先级的任务处于就绪状态,该任务将会被抢占,重新放回到就绪状态。

AliOS Things允许任务处于组合状态:阻塞挂起状态或休眠挂起状态:

(1) 任务在阻塞状态下,被其他任务挂起,则进入阻塞挂起状态。该状态下,若任务被恢复则保持阻塞状态;若任务解除阻塞则保持挂起状态。

(2) 任务在休眠状态下,被其他任务挂起,则进入休眠挂起状态。该状态下,若任务被恢复则保持休眠状态;若任务休眠到期则保持挂起状态。

注意,虽然FreeRTOS中处于阻塞状态的任务可以被挂起,但是被挂起后任务不再阻塞,即使任务等待的事件未发生但任务一旦被恢复,将进入就绪状态。而AliOS Things中任务因等待某个事件进入阻塞状态,而此时又被其他任务将其挂起,该任务仍然是处于阻塞状态,如果在此过程中等待的事件发生了,则任务会解除阻塞进入挂起状态;如果事件未发生,则任务恢复状态后仍然处于阻塞状态。这样设计的好处是可以保证任务解除阻塞是等待的特定事件发生了而引起的,而非因为任务挂起恢复后消除了等待状态造成的。例如,任务A和任务B同时访问资源M,由于任务B先拿到互斥量导致任务A因未获得互斥量而进入阻塞状态,之后任务C将任务A挂起,然后恢复,此时任务A仍处于阻塞状态并无法访问资源M,这样能够保证资源M的互斥访问。如果采用FreeRTOS的任务状态切换方式,任务A在挂起恢复后将直接进入就绪状态,这样任务A可能会在任务B还未操作完成时就访问了资源M,使得互斥量未起到互斥的作用。

3、示例

下面以一个应用示例来说明AliOS Things中任务状态切换过程,如图8所示:

(1) 在t0时刻,任务task1、task2分别通过aos_task_new()和aos_task_new_ext()函数调用被创建,之后task1进入就绪状态,而task2处于挂起状态。

(2) Task1得到运行后,在t1时刻调用aos_task_resume()将task2恢复,task2进入就绪状态,之后task1通过调用aos_msleep()进入休眠状态,task2因为task1休眠而获得CPU执行权,task2运行后因等待信号量进入阻塞状态。

(3) Task1在t2时刻因延迟到期得到运行,并调用aos_task_suspend()将task2挂起,task2此时的状态为阻塞挂起。之后task1通过调用aos_msleep()进入休眠状态。

(4) Task1在t3时刻因延迟到期得到运行,并调用aos_task_resume()将task2恢复,此时task2的状态为阻塞状态。之后task1通过调用aos_msleep()进入休眠状态。

(5) Task1在t4时刻因延迟到期得到运行,并调用aos_sem_signal()释放信号量,这时task2因等到信号量而进入就绪状态。待到task1再次进入休眠转改后task2得到运行,进入运行状态。

image.png

图8 AliOS Things任务状态和切换应用示例

根据图8描述的应用示例,编写代码如下:

include <aos/kernel.h>

static aos_sem_t g_testsync_sem;
static aos_task_t handle_task2 = {NULL};
void task1_entry()
{

printf("task1 is running!\n");
printf("task1 resume task2!\n");
if (0 != aos_task_resume(&handle_task2))
{
    printf("task1 resume task2 failed!\n");
}
printf("task1 start to sleep and release CPU!\n");
aos_msleep(10000);

printf("task1 suspend task2!\n");
if (0 != aos_task_suspend(&handle_task2))
{
    printf("task1 resume task2 failed!\n");
}
printf("task1 start to sleep and release CPU!\n");
aos_msleep(10000);

printf("task1 resume task2 again!\n");
if (0 != aos_task_resume(&handle_task2))
{
    printf("task1 resume task2 failed!\n");
}
printf("task1 start to sleep and release CPU!\n");
aos_msleep(10000);
printf("task1 signal a semphone!\n");
aos_sem_signal(&g_testsync_sem);
printf("task1 start to sleep and release CPU!\n");
aos_msleep(10000);
aos_task_exit(0);

}

void task2_entry()
{

printf("task2 is running!\n");
if (0 != aos_sem_wait(&g_testsync_sem, AOS_WAIT_FOREVER))
{
    printf("task2 wait semphone failed!\n");
}

printf("task2 get semphone and is running!\n");
aos_task_exit(0);

}

int task_test(void)
{

int retn;

retn = aos_sem_new(&g_testsync_sem, 0);
if (retn != 0)
{
    printf("%s:%d sem new failed, err=%d\n", __func__, __LINE__, retn);
    return -1;
}
if (0 != aos_task_new("task1", task1_entry, NULL, 4096))
{
    aos_sem_free(&g_testsync_sem);
    return -1;
}

if (0 != aos_task_new_ext(&handle_task2, "task2", task2_entry, NULL, 4096, 30, 0))
{
    aos_sem_free(&g_testsync_sem);
    return -1;
}

return 0;

}
上述代码的运行结果如图9。

image.png
图9 应用示例代码运行结果

图9中,task2在运行之后因为未信号量计数值为0而进入阻塞状态,此时通过tasklist命令查看task2的任务状态为“PEND”,这里的“PEND”代表阻塞状态;之后task1将task2挂起,此时查看到的task2任务状态为“PEND_SUS”代表阻塞挂起状态;最后task1恢复task2,此时查到task2任务状态为“PEND”,说明代码运行结果与AlisOS Things任务状态图中的切换过程是一致的。

4、参考
【1】https://freertos.org/Documentation/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

【2】https://www.rt-thread.org/document/site/programming-manual/thread/thread/#_12

【3】《Real-time Operating Systems》Jim Cooling.

5、开发者技术支持
如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号
image.png

相关文章
|
1月前
|
存储 人工智能 算法
操作系统的演化之路:从单一任务到多任务处理
【8月更文挑战第12天】 在计算机科学的历史长河中,操作系统作为硬件与软件之间的桥梁,其发展经历了由简单到复杂、由单一到多元的转变。本文旨在探究操作系统如何实现从执行单个任务到同时管理多个任务的飞跃,并分析这一变革对现代计算技术的影响。通过回顾操作系统的关键发展阶段,我们将理解多任务处理机制的起源和优化过程,以及它如何塑造了今天的数字世界。
|
8天前
|
机器学习/深度学习 人工智能 算法
操作系统的未来:从多任务到深度学习的演变之路
本文将探讨操作系统如何从处理简单多任务发展到支持复杂的深度学习任务。我们将分析现代操作系统面临的新挑战,以及它们如何适应人工智能和大数据时代的要求。文章不仅回顾过去,也展望未来,思考操作系统在技术演进中的角色和方向。
21 3
|
11天前
|
人工智能 算法 数据挖掘
操作系统的演变:从单任务到多任务的旅程
操作系统(OS)是计算机系统的核心,它管理硬件资源、提供用户界面并运行应用程序。本文将探讨操作系统如何从单任务环境演变为支持多任务的环境,包括这一过程中的技术挑战和解决方案。我们将看到,随着计算需求的增长,操作系统必须适应更复杂的任务管理和资源分配策略,以提高效率和用户体验。通过这个旅程,我们不仅能够理解操作系统的发展,还能洞察未来可能的趋势。
24 5
|
28天前
|
机器学习/深度学习 人工智能 安全
操作系统的未来:从多任务处理到人工智能
【8月更文挑战第23天】本文将探讨操作系统的发展历程及其未来趋势,特别是人工智能在操作系统中的应用。我们将看到如何通过引入人工智能技术,操作系统能够更加智能化地管理资源,提高系统性能和用户体验。
|
1月前
|
Web App开发 机器学习/深度学习 物联网
操作系统的演变:从单任务到现代多任务系统
【8月更文挑战第15天】操作系统作为计算机硬件与软件之间的桥梁,其设计和发展反映了计算技术的进步。本文将探讨操作系统的演变过程,从早期的单一任务处理到现代复杂的多任务和多用户环境。我们将分析这一转变如何影响用户体验、资源管理和系统安全性,并展望操作系统未来的可能发展方向。
46 2
|
1月前
|
安全 物联网 调度
操作系统的演变:从单一任务到多任务处理
【8月更文挑战第9天】在数字时代的浪潮中,操作系统的发展经历了从单任务到多任务处理的转变。本文将探讨这一转变背后的原因、影响及其对未来技术趋势的启示。我们将通过分析操作系统的架构变化,揭示其如何适应日益增长的计算需求和用户期望。文章还将讨论多任务处理对软件开发、用户体验和系统性能的影响,以及这些变化如何塑造了我们今天所使用的技术。
|
1月前
|
安全 Unix 物联网
操作系统的演进:从单任务到现代多任务环境
在数字时代的心脏跳动着的是操作系统,它是计算机硬件与软件之间的桥梁。本文将探讨操作系统如何从简单的单任务处理发展至复杂的多任务和多用户环境。我们将穿越时空,回顾那些定义了现代计算的关键时刻,见证技术创新如何塑造我们的数字生活。
|
1月前
|
人工智能 并行计算 大数据
操作系统的演变之路:从单任务到多任务再到并行计算
本文将探讨操作系统(OS)的发展脉络,着重分析从早期单任务系统到现代多任务和并行处理系统的演进。我们将通过浅显易懂的语言和比喻,揭示这一技术变革如何影响我们的日常生活和工作方式,并展望未来可能的发展趋势。
|
1月前
|
机器学习/深度学习 人工智能 物联网
操作系统的演变:从单任务到多任务的旅程
【8月更文挑战第9天】 在数字时代的浪潮中,操作系统作为计算机技术的核心,其发展历史充满了创新与变革。本文将通过探索操作系统从简单的单任务处理到复杂多任务处理的转变过程,揭示技术进步如何塑造现代计算体验。我们将穿越时间线,见证操作系统设计理念的演进,以及这一进程如何影响软件开发、用户交互和系统性能。文章旨在为读者提供深入理解,展示操作系统如何适应不断增长的性能需求和技术挑战,同时预测未来可能的发展方向。
27 1
|
1月前
|
存储 算法 调度
操作系统的演变:从单任务到多任务再到现代操作系统
【8月更文挑战第8天】随着科技的发展,操作系统经历了从单任务到多任务,再到现代操作系统的演变。本文将探讨这一演变过程中的关键技术和概念,以及它们对现代计算机系统的影响。