Skip to content

modm 的上下文切换实现

· 12 min

under construction!#

上下文存储#

/// 纤程上下文存储了关于栈的信息。
struct modm_context_t
{
uintptr_t *sp; ///< 当前上下文的栈指针 (仅在上下文不运行时有效)
uintptr_t *bottom; ///< 栈的底部地址
uintptr_t *top; ///< 栈的顶部地址(超出一字)
};

上下文切换相关函数#

/**
* @ingroup modm_processing_fiber
* @defgroup modm_processing_fiber_context 纤程上下文函数
*
* 这些函数实现了与架构相关的底层操作,用于初始化、启动、挂起和结束纤程。
* 它们可以用于实现替代的调度策略,或在 modm 之外使用纤程功能。
* @{
*/
/**
* 使用存储在顶部的函数指针和参数初始化上下文。
* `ctx->sp` 被设置为栈顶下两个 `uintptr_t`。
* 必须仅调用一次来初始化栈。
*
* @param fn 函数指针,指向一个 `void(*)(uintptr_t)` 函数。
*/
void
modm_context_init(modm_context_t *ctx,
uintptr_t *bottom, uintptr_t *top,
uintptr_t fn, uintptr_t arg);
/**
* 重置上下文的栈指针到正确的偏移位置,为跳转到纤程做准备。
* 必须在初始化之后调用,并且在每次从头开始执行函数时调用。
*/
void
modm_context_reset(modm_context_t *ctx);
/**
* 将控制权从主上下文切换到纤程上下文。
* 该函数会初始化硬件,然后从调用方上下文跳转到 `to` 纤程。
*/
uintptr_t
modm_context_start(modm_context_t *to);
/**
* 将上下文压栈到 `from->sp`,并从 `to->sp` 弹出上下文,
* 从一个纤程跳转到另一个纤程。
*/
void
modm_context_jump(modm_context_t *from, modm_context_t *to);
/**
* 将控制权从纤程上下文切换回主上下文。
* 控制流将继续在主上下文中,从 `modm_context_start()` 函数返回。
*/
void modm_noreturn
modm_context_end(uintptr_t retval);
/**
* 将寄存器文件清零,并为栈的其余部分设置水印。
* 你可以在调用 `modm_context_reset()` 之前或之后调用此函数,
* 但**不能**在纤程运行时调用!
*/
void
modm_context_stack_watermark(modm_context_t *ctx);
/**
* 通过从栈底开始搜索水印位置,返回栈的使用量。
* 你可以在调用 `modm_context_stack_watermark()` 之后的任何时候调用此函数。
*/
size_t
modm_context_stack_usage(const modm_context_t *ctx);
/// @}

涉及的寄存器#

涉及的汇编指令#

上下文切换的实现#

#define modm_naked __attribute__((naked)) // 禁止编译器插入栈帧管理代码
#define MODM_PUSH_CONTEXT() \
"push {r4-r11, lr} \n\t" \
"vpush {d8-d15} \n\t"
#define MODM_POP_CONTEXT() \
"vpop {d8-d15} \n\t" \
"pop {r4-r11, pc} \n\t"
constexpr size_t StackWordsReset = 1;
constexpr size_t StackWordsStorage = 2;
constexpr size_t StackWordsRegisters = 25;
constexpr size_t StackWordsAll = StackWordsStorage + StackWordsRegisters;
constexpr size_t StackSizeWord = sizeof(uintptr_t);
constexpr uintptr_t StackWatermark = 0xf00d'cafe;
void modm_naked
modm_context_entry()
{
asm volatile
(
"ldm sp, {r0, pc} \n\t" // 从栈中加载 R0 和程序计数器(跳转到函数)
);
}
void modm_context_init(modm_context_t *ctx,
uintptr_t *bottom, uintptr_t *top,
uintptr_t fn, uintptr_t fn_arg)
{
ctx->bottom = bottom;
ctx->top = top;
ctx->sp = top;
*--ctx->sp = fn; // 保存函数地址
*--ctx->sp = fn_arg; // 保存函数参数
}
void modm_context_reset(modm_context_t *ctx)
{
*ctx->bottom = StackWatermark; // 标记栈底
ctx->sp = ctx->top - StackWordsStorage;
*--ctx->sp = (uintptr_t) modm_context_entry; // 保存入口函数地址
ctx->sp -= StackWordsRegisters - StackWordsReset; // 为寄存器区域保留栈空间
}
void modm_context_stack_watermark(modm_context_t *ctx)
{
for (auto *word = ctx->top - StackWordsAll;
word < ctx->top - StackWordsStorage - StackWordsReset; word++)
*word = 0; // 清空寄存器区域栈空间
for (auto *word = ctx->bottom; word < ctx->top - StackWordsAll; word++)
*word = StackWatermark; // 将栈空间填充为水印值
}
size_t modm_context_stack_usage(const modm_context_t *ctx)
{
for (auto *word = ctx->bottom; word < ctx->top; word++)
if (StackWatermark != *word)
return (ctx->top - word) * StackSizeWord;
return 0;
}
uintptr_t modm_naked
modm_context_start(modm_context_t*)
{
asm volatile
(
MODM_PUSH_CONTEXT() // 保存当前上下文
"mrs r1, control \n\t" // 获取 CONTROL 寄存器
"orr r1, r1, #2 \n\t" // 设置 CONTROL 的 SPSEL 位,切换到 PSP
"msr control, r1 \n\t"
"ldr sp, [r0] \n\t" // 从上下文加载新栈指针
MODM_POP_CONTEXT() // 恢复新上下文
);
}
void modm_naked
modm_context_jump(modm_context_t*, modm_context_t*)
{
asm volatile
(
MODM_PUSH_CONTEXT() // 保存当前上下文
"ldr r3, [r0, #4] \n\t" // 加载当前上下文的栈底地址
"str sp, [r0] \n\t" // 保存当前栈指针
"cmp sp, r3 \n\t" // 检查栈是否溢出
"ldr sp, [r1] \n\t" // 加载目标上下文的栈指针
MODM_POP_CONTEXT() // 恢复目标上下文
);
}