4.2.5 任务时间片
4.2.5 任务时间片
📚 本节导读
学习时长: 约 15 分钟
难度级别: ⭐⭐⭐☆☆
前置知识: 任务调度机制、优先级概念
🎯 学习目标
- 理解时间片的概念和作用
- 掌握同优先级任务的时间片轮转调度原理
- 掌握
os_task_set_time_slice()和os_task_get_time_slice()等 API - 了解时间片的 Kconfig 配置
一、时间片概念
1.1 什么是时间片?
时间片(Time Slice)是分配给同优先级任务的一个 CPU 时间配额。当一个任务的时间片用完时,调度器会将其移到同优先级就绪链表的末尾,让下一个同优先级任务运行。
时间片的单位是系统时钟节拍(tick)。例如,如果 OS_TICK_PER_SECOND = 100,一个时钟节拍就是 10ms,那么时间片值为 10 就表示 100ms 的 CPU 时间。
1.2 时间片的作用
同优先级就绪队列 (优先级 5):
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 任务 A │───►│ 任务 B │───►│ 任务 C │
│ 时间片:10│ │ 时间片:5 │ │ 时间片:10│
└─────────┘ └─────────┘ └─────────┘
│ │ │
▼ ▼ ▼
运行 10 tick 运行 5 tick 运行 10 tick
│ │ │
└──────────────┴──────────────┘
时间片用完,轮转到下一个时间片机制确保了同优先级任务之间的公平性——每个任务都能获得 CPU 时间,不会出现某个任务独占 CPU 导致其他同优先级任务饥饿的情况。
二、时间片的配置
2.1 Kconfig 配置
在 kernel/Kconfig 中,时间片的默认值通过 OS_SCHEDULE_TIME_SLICE 配置:
config OS_SCHEDULE_TIME_SLICE
int "Task time slice(unit: tick)"
range 1 100
default 10| 配置项 | 默认值 | 范围 | 说明 |
|---|---|---|---|
OS_SCHEDULE_TIME_SLICE | 10 | 1 ~ 100 | 新创建任务的默认时间片(单位:tick) |
2.2 时间片在 TCB 中的存储
每个任务的 TCB 中维护两个时间片相关字段:
os_tick_t time_slice; /* 任务的总时间片(每次启动时重置为此值) */
os_tick_t remaining_time_slice; /* 当前剩余时间片(随运行递减) */任务创建时,两个字段都被初始化为 OS_SCHEDULE_TIME_SLICE。每次任务被调度运行时,remaining_time_slice 从 time_slice 开始递减。当 remaining_time_slice 减到 0 时,触发时间片轮转。
三、时间片 API
3.1 os_task_set_time_slice() —— 设置时间片
os_err_t os_task_set_time_slice(os_task_id tid, os_tick_t new_time_slice);参数:
tid:目标任务的任务 IDnew_time_slice:新的时间片值,必须 > 0 且 < 100
行为:
- 更新
time_slice字段 - 如果任务处于 INIT 状态(尚未启动),同时更新
remaining_time_slice - 如果任务已经在运行,
remaining_time_slice保持不变(下次被调度时使用新时间片)
/* 为任务设置 5 个 tick 的时间片 */
os_task_set_time_slice(tid, 5);3.2 os_task_get_time_slice() —— 获取时间片
os_err_t os_task_get_time_slice(os_task_id tid, os_tick_t *time_slice);获取任务的总时间片(即 time_slice 字段的值)。
os_tick_t slice;
os_task_get_time_slice(tid, &slice);
os_kprintf("Time slice: %lu ticks\r\n", slice);3.3 os_task_get_remaining_time_slice() —— 获取剩余时间片
os_err_t os_task_get_remaining_time_slice(os_task_id tid, os_tick_t *time_slice);获取任务当前剩余的可用时间片(即 remaining_time_slice 字段的值)。
os_tick_t remaining;
os_task_get_remaining_time_slice(tid, &remaining);
os_kprintf("Remaining time slice: %lu ticks\r\n", remaining);3.4 使用示例
void monitor_task(void *arg)
{
os_task_id tid_a = os_task_get_id("task_a");
os_task_id tid_b = os_task_get_id("task_b");
/* 为 task_a 分配更多 CPU 时间 */
os_task_set_time_slice(tid_a, 20); /* 200ms(假设 tick=100Hz) */
/* 为 task_b 分配较少 CPU 时间 */
os_task_set_time_slice(tid_b, 5); /* 50ms */
while (1)
{
os_tick_t rem_a, rem_b;
os_task_get_remaining_time_slice(tid_a, &rem_a);
os_task_get_remaining_time_slice(tid_b, &rem_b);
os_kprintf("A: %lu/%lu B: %lu/%lu\r\n", rem_a,
/* 需要单独获取总时间片 */ 0,
rem_b, 0);
os_task_msleep(1000);
}
}四、时间片与优先级的关系
4.1 优先级优先
优先级抢占的优先级(pun intended)高于时间片轮转。具体表现为:
- 只要有更高优先级的任务就绪,当前任务会被立即抢占,无论其时间片是否用完
- 时间片轮转仅在同优先级任务之间起作用
- 不同优先级的任务之间不存在时间片轮转
4.2 调度决策流程
系统时钟节拍中断
│
▼
┌─────────────┐
│ 时间片递减 │
│ remaining-- │
└──────┬──────┘
│
remaining == 0?
│
┌────┴────┐
│ 是 │ 否
▼ ▼
┌──────────┐ ┌──────────┐
│ 移到同优先级│ │ 检查是否有 │
│ 队列尾部 │ │ 更高优先级 │
│ 设置同优先级│ │ 任务就绪 │
│ 调度标记 │ └─────┬────┘
└─────┬────┘ │
│ ┌────┴────┐
│ │ 是 │ 否
│ ▼ ▼
│ ┌────────┐ ┌────────┐
│ │ 触发抢占│ │ 继续运行│
│ └────────┘ └────────┘
│
▼
┌──────────┐
│ 触发调度 │
└──────────┘4.3 时间片耗尽时的行为
当同优先级任务的时间片耗尽时,调度器通过 k_readyq_move_tail() 将当前任务移到就绪队列末尾,并设置 g_os_same_prio_sched_bit(SMP)或直接触发 OS_KERNEL_EXIT_SCHED()(单核)。
五、时间片配置建议
| 场景 | 建议时间片 | 说明 |
|---|---|---|
| 快速响应任务 | 1 ~ 3 tick | 需要快速轮转,如多个同优先级 IO 处理任务 |
| 一般计算任务 | 5 ~ 10 tick | 默认配置,平衡性能和响应 |
| 批量处理任务 | 10 ~ 50 tick | 减少切换开销,提高吞吐量 |
| 极长批处理 | 50 ~ 100 tick | 大块数据处理,但需注意影响同优先级任务响应 |
注意:时间片设置过小会导致频繁的任务切换,增加上下文切换开销;时间片设置过大可能导致同优先级任务响应变慢。
📝 本节小结
时间片是 OneOS 实现同优先级任务公平调度的机制。每个任务拥有一个时间片配额,由 time_slice 和 remaining_time_slice 两个字段共同管理。默认时间片通过 OS_SCHEDULE_TIME_SLICE 配置,可在运行时通过 os_task_set_time_slice() 动态调整。时间片轮转仅在同优先级任务之间生效,优先级抢占始终优先于时间片轮转。