要完成C functions的context switch要如何實現呢?我的初步想法是要透過組合語言,完成一個context_save(),將對應的EIP, ESP, EBP以及IA32 ABI所規定的暫存器保存起來(其他CPU架構會有類似的規範),然後再完成另一個context_restore(),可以將指定的context恢復,然後就可依此實現出context_switch(cur_contxt, next_context),就像專業嵌入式軟件開發一書所示範的一樣。
在Linux中可以直接採用getcontext(), setcontext(), makecontext()以及swapcontext()來完成這個工作。在man page中,甚至還附了一個簡單的範例,wikipedia也有一篇文章說明。
這篇文章主要是因為我對於man page與wikipedia的說明一開始有點不清楚,所以將man page的範例程式稍作修改,確認一下自己的理解無誤。由於這幾個functions的執行路徑並不是那麼的sequential,所以紀錄下來,免得自己以後忘記。:P
在Linux中可以直接採用getcontext(), setcontext(), makecontext()以及swapcontext()來完成這個工作。在man page中,甚至還附了一個簡單的範例,wikipedia也有一篇文章說明。
這篇文章主要是因為我對於man page與wikipedia的說明一開始有點不清楚,所以將man page的範例程式稍作修改,確認一下自己的理解無誤。由於這幾個functions的執行路徑並不是那麼的sequential,所以紀錄下來,免得自己以後忘記。:P
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
static ucontext_t uctx_main, uctx_func1, uctx_func2;
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
static void func1(void)
{
printf("func1: started\n");
printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n");
if (swapcontext(&uctx_func1, &uctx_func2) == -1)
handle_error("swapcontext");
printf("func1: returning\n");
}
static void func2(void)
{
printf("func2: started\n");
printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n");
if (swapcontext(&uctx_func2, &uctx_func1) == -1)
handle_error("swapcontext");
printf("func2: returning\n");
}
int main(int argc, char *argv[])
{
char func1_stack[16384];
char func2_stack[16384];
getcontext(&uctx_main);
if (getcontext(&uctx_func1) == -1)
handle_error("getcontext");
uctx_func1.uc_stack.ss_sp = func1_stack;
uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
uctx_func1.uc_link = &uctx_main;
makecontext(&uctx_func1, func1, 0);
if (getcontext(&uctx_func2) == -1)
handle_error("getcontext");
uctx_func2.uc_stack.ss_sp = func2_stack;
uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
/* Successor context is f1(), unless argc > 1 */
uctx_func2.uc_link = &uctx_func1;
makecontext(&uctx_func2, func2, 0);
printf("main: swapcontext(&uctx_main, &uctx_func2)\n");
if (setcontext(&uctx_func2) == -1)
handle_error("swapcontext");
printf("main: exiting\n");
exit(EXIT_SUCCESS);
}
|
- L34:getcontext()會在"第一次"呼叫時將uctx_main初始化。
- L36 - L41:設定uctx_func1,讓func1的context存於uctx_func1中,並指定function執行結束後的下一個context是uctx_main。
- L43 - L49:設定uctx_func2,讓func2的context存於uctx_func2中,並指定function執行結束後的下一個context是uctx_func2。
- L52:將context切換到uctx_func2,於是func2開始執行。
- L23:func2執行到L23後,切換到func1的context,於是func1開始執行。
- L14:func1執行到L14後,又切回func2的context,於是func2之前的context,func2進行函式退出動作,退出後func2,由於有指定func2執行完後要切換到func1的context,於是回到func1。
- L16:退出func1,由於有指定func1執行完後的下一個context,所以跳到main function的context
有趣的是,此時main function的context在那邊呢?在L34的getcontext()!!
getcontext()等於是一個標記,只要返回到該main context,就會直接返回到L34。於是上述流程再次執行,如下的輸出便不斷出現:
main: swapcontext(&uctx_main, &uctx_func2)
func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: started
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: returning
func1: returning
main: swapcontext(&uctx_main, &uctx_func2)
func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
...
有趣吧?連一行while都沒有,就可以讓多個functions輪流不斷執行,如果再加上執行期動態調整切換的context,就等於有了一個task scheduler。:-)
有什麼應用需要使用context switch呢?可多了~尤其是當我們要實現某些語言沒有內建的功能來簡化編程模型時,像是coroutine、multitasking...,就算已有現成的程式庫可用,但了解一下其實現原理也蠻有趣的。
留言
張貼留言