4.2.6 任务挂起与恢复
4.2.6 任务挂起与恢复
📚 本节导读
学习时长: 约 15 分钟
难度级别: ⭐⭐☆☆☆
前置知识: 任务状态机、SUSPEND 状态概念
🎯 学习目标
- 理解挂起(SUSPEND)状态的含义和作用
- 掌握
os_task_suspend()和os_task_resume()的用法 - 了解挂起状态对调度的影响
- 理解挂起与其他状态的叠加行为
一、挂起状态概述
挂起(SUSPEND)是 OneOS 任务状态中一个特殊的状态,它表示任务被手动暂停执行。与 SLEEP 和 BLOCK 不同,挂起不是由任务自身行为(延时或等待资源)触发的,而是由外部显式调用 os_task_suspend() 触发。
1.1 挂起状态的特点
| 特性 | 说明 |
|---|---|
| 触发方式 | 通过 os_task_suspend() 手动触发 |
| 可叠加 | 可与 SLEEP、BLOCK 状态叠加 |
| 解除方式 | 必须通过 os_task_resume() 显式解除 |
| 调度影响 | 挂起后任务从就绪队列中移除,不再参与调度 |
| 自己挂自己 | 允许,但会立即触发调度切换到其他任务 |
1.2 挂起与其他状态的区别
| 状态 | 触发方式 | 恢复方式 | 是否可叠加挂起 |
|---|---|---|---|
| SLEEP | 调用延时函数 | 延时到期自动恢复 | ✅ 是 |
| BLOCK | 等待 IPC 资源 | 资源可用自动恢复 | ✅ 是 |
| SUSPEND | 调用 os_task_suspend() | 调用 os_task_resume() | N/A |
| READY | 任务就绪 | 被调度选中 | ✅ 是 |
二、os_task_suspend() —— 挂起任务
2.1 函数签名
os_err_t os_task_suspend(os_task_id tid);2.2 返回值
| 返回值 | 说明 |
|---|---|
OS_SUCCESS | 挂起成功 |
OS_FAILURE | 挂起失败(任务状态不正确) |
2.3 内部实现流程
os_err_t os_task_suspend(os_task_id tid)
{
/* 检查任务状态:只允许 READY、SLEEP、BLOCK 状态的任务被挂起 */
if (task->state & ~(OS_TASK_STATE_READY | OS_TASK_STATE_SLEEP | OS_TASK_STATE_BLOCK))
{
/* 任务状态不正确(如已处于 CLOSE、INIT 等状态) */
return OS_FAILURE;
}
/* 如果任务在就绪队列中,移除 */
if (task->state & OS_TASK_STATE_READY)
{
k_readyq_remove(task);
task->state &= ~OS_TASK_STATE_READY;
}
/* 添加 SUSPEND 状态 */
task->state |= OS_TASK_STATE_SUSPEND;
/* 如果挂起的是自己,触发调度 */
if (task == _k_task_self())
{
OS_KERNEL_EXIT_SCHED();
}
return OS_SUCCESS;
}2.4 使用限制
- 不允许在中断上下文中挂起自己(中断禁用或调度锁定时也不行)
- 任务必须处于 READY、SLEEP 或 BLOCK 状态才能被挂起
- 不能挂起处于 INIT 或 CLOSE 状态的任务
2.5 使用示例
/* 挂起另一个任务 */
os_task_id tid = os_task_get_id("worker_task");
if (tid != OS_NULL)
{
os_err_t ret = os_task_suspend(tid);
if (ret == OS_SUCCESS)
{
os_kprintf("Task suspended\r\n");
}
}
/* 任务挂起自己 */
void self_suspend_task(void *arg)
{
while (1)
{
/* 做一些工作 */
do_work();
/* 挂起自己,等待被其他任务恢复 */
os_task_suspend(os_get_current_task());
/* 被恢复后继续执行 */
os_kprintf("Task resumed!\r\n");
}
}三、os_task_resume() —— 恢复任务
3.1 函数签名
os_err_t os_task_resume(os_task_id tid);3.2 返回值
| 返回值 | 说明 |
|---|---|
OS_SUCCESS | 恢复成功 |
OS_FAILURE | 恢复失败(任务不在 SUSPEND 状态) |
3.3 内部实现流程
os_err_t os_task_resume(os_task_id tid)
{
/* 检查任务是否处于 SUSPEND 状态 */
if ((task->state & OS_TASK_STATE_SUSPEND) == 0)
{
return OS_FAILURE;
}
/* 清除 SUSPEND 状态 */
task->state &= ~OS_TASK_STATE_SUSPEND;
/* 如果任务不在 SLEEP/BLOCK 状态,转为 READY 并加入就绪队列 */
if ((task->state & (OS_TASK_STATE_SLEEP | OS_TASK_STATE_BLOCK)) != 0)
{
/* 任务仍在延时或等待资源,只清除挂起标志 */
/* 等延时到期或资源可用后自动回到 READY */
}
else
{
/* 任务可以直接就绪 */
task->state |= OS_TASK_STATE_READY;
k_readyq_put(task);
/* 触发调度 */
}
return OS_SUCCESS;
}3.4 恢复后的行为
| 挂起前的状态 | 恢复后的行为 |
|---|---|
| 仅 SUSPEND | 清除 SUSPEND → 转为 READY → 加入就绪队列 |
| SLEEP + SUSPEND | 清除 SUSPEND → 保留 SLEEP → 延时到期后自动 READY |
| BLOCK + SUSPEND | 清除 SUSPEND → 保留 BLOCK → 资源可用后自动 READY |
3.5 使用示例
/* 控制任务:挂起和恢复 worker 任务 */
os_task_id worker_tid;
void controller_task(void *arg)
{
worker_tid = os_task_get_id("worker_task");
while (1)
{
/* 暂停 worker 任务 */
os_task_suspend(worker_tid);
os_kprintf("Worker paused\r\n");
/* 等待一段时间 */
os_task_msleep(5000);
/* 恢复 worker 任务 */
os_task_resume(worker_tid);
os_kprintf("Worker resumed\r\n");
os_task_msleep(5000);
}
}四、典型应用场景
4.1 任务间同步控制
使用挂起/恢复实现简单的任务间同步:
/* 生产者-消费者模式中的流控 */
os_task_id consumer_tid;
void producer_task(void *arg)
{
while (1)
{
produce_data();
/* 唤醒消费者处理数据 */
os_task_resume(consumer_tid);
}
}
void consumer_task(void *arg)
{
consumer_tid = os_get_current_task();
while (1)
{
consume_data();
/* 挂起自己,等待生产者唤醒 */
os_task_suspend(os_get_current_task());
}
}注意:对于更复杂的任务间同步,建议使用信号量或事件标志组,它们提供了更丰富的同步语义。
4.2 调试与诊断
在调试时挂起可疑任务,便于分析系统状态:
/* 调试命令:挂起指定任务 */
void debug_suspend_task(const char *name)
{
os_task_id tid = os_task_get_id(name);
if (tid != OS_NULL)
{
os_task_suspend(tid);
os_kprintf("Task '%s' suspended for debugging\r\n", name);
}
}4.3 电源管理
在低功耗场景下挂起非关键任务:
void power_save_mode(void)
{
/* 挂起所有非关键任务 */
os_task_suspend(log_task);
os_task_suspend(monitor_task);
os_task_suspend(display_task);
/* 进入低功耗模式 */
enter_low_power();
/* 唤醒后恢复任务 */
os_task_resume(log_task);
os_task_resume(monitor_task);
os_task_resume(display_task);
}五、注意事项
不要滥用挂起:挂起/恢复是一种粗暴的控制方式,可能导致数据丢失或状态不一致。对于同步需求,优先使用信号量、事件标志组等 IPC 机制。
挂起持有资源的任务:如果挂起的任务持有互斥锁或其他共享资源,可能导致其他任务阻塞甚至死锁。挂起前应确保任务已释放所有资源。
多次挂起不支持:OneOS 不支持嵌套挂起。如果对一个已经挂起的任务再次调用
os_task_suspend(),会返回OS_FAILURE。恢复未挂起的任务:如果任务不在 SUSPEND 状态,调用
os_task_resume()会返回OS_FAILURE。挂起自己:任务可以挂起自己,但挂起后立即触发调度,后续代码在任务被恢复后才继续执行。
📝 本节小结
挂起(SUSPEND)是 OneOS 中一种手动暂停任务执行的机制,通过 os_task_suspend() 和 os_task_resume() 控制。SUSPEND 状态可以与其他状态(SLEEP、BLOCK)叠加,恢复时如果任务仍在延时或等待资源,则只清除 SUSPEND 标志。挂起/恢复适用于简单的任务控制、调试和电源管理场景,但对于复杂的同步需求,建议使用 OneOS 提供的 IPC 机制。