Open BruceChen7 opened 4 years ago
在libtask中,虽说是一个库,但是其包含了一个main函数,这和常规的库有点不一样,看看使用libtask的特点:
/* * gcc count_test.c -o count_test -L. -ltask -I./ */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <task.h> void counttask1(void *arg) { int i; for( i = 0; i < 5; i++) { printf("task1: %d\n", i); taskyield(); } } void counttask2(void *arg) { int i; for( i = 5; i < 10; i++) { printf("task2: %d\n", i); taskyield(); } } void taskmain(int argc, char **argv) { taskcreate(counttask1, NULL, 32768); taskcreate(counttask2, NULL, 32768); }
在注释中,给出了编译的方式,也就是说我们在利用libtask来做自己的应用程序的时候,需要自己从taskmain函数开始。
libtask中的main
该main函数的作用,调用taskmain作为第一个执行的coroutine,调度整个coroutine链。
int main(int argc, char **argv) { struct sigaction sa, osa; // 信号处理函数 memset(&sa, 0, sizeof sa); sa.sa_handler = taskinfo; sa.sa_flags = SA_RESTART; sigaction(SIGQUIT, &sa, &osa); #ifdef SIGINFO sigaction(SIGINFO, &sa, &osa); #endif argv0 = argv[0]; // 都是全局变量 taskargc = argc; taskargv = argv; // main coroutine if(mainstacksize == 0) mainstacksize = 256*1024; // 创建main任务 // 25k的栈空间 taskcreate(taskmainstart, nil, mainstacksize); // 开始执行调度 taskscheduler(); // 不可能出现 fprint(2, "taskscheduler returned in main!\n"); abort(); return 0; } static void taskmainstart(void *v) { taskname("taskmain"); taskmain(taskargc, taskargv); }
main函数在执行完taskmain routine后,就开始执行taskscheduler,这个是整个libtask的核心,
taskscheduler
static void taskscheduler(void) { int i; Task *t; taskdebug("scheduler enter"); // 调度程序一直从任务列表中挑选任务 for(;;){ // 如果没有任务,直接进程退出 if(taskcount == 0) exit(taskexitval); // 获取队列头 t = taskrunqueue.head; if(t == nil){ fprint(2, "no runnable tasks! %d tasks stalled\n", taskcount); exit(1); } // 从运行任务中,删除该任务 deltask(&taskrunqueue, t); t->ready = 0; // 更新当前任务列表 taskrunning = t; tasknswitch++; taskdebug("run %d (%s)", t->id, t->name); // 切到新的context contextswitch(&taskschedcontext, &t->context); taskrunning = nil; // 进行资源释放 if(t->exiting){ // 减少当前任务列表 if(!t->system) // 如果不是系统任务,那么直接将任务数-1 taskcount--; i = t->alltaskslot; alltask[i] = alltask[--nalltask]; alltask[i]->alltaskslot = i; free(t); } } }
在这里我们搜索下taskschedcontext,这个变量:
task.c|15| Context taskschedcontext; task.c|190| contextswitch(&taskrunning->context, &taskschedcontext); task.c|278| contextswitch(&taskschedcontext, &t->context);
跟踪一下该变量的使用,知道其是当前main coroutine的执行上下文,当从main coroutine中的调度器调度到别的coroutine上下文的时候,保存到taskschedcontext中(swapcontext中有有这个作用),这是个全局变量。
contextswitch
在linux中的uontext.h,提供的getcontext,setcontext,swapcontext和makecontext函数,具体的上下文有结构体ucontext_t指定,包括任务的栈的大小,栈顶指针以及cpu内寄存器信息。
我们看下contextswitch的实现
static void contextswitch(Context *from, Context *to) { // 直接使用swapcontext来执行上下文切换 // 保存当前上下文到from,并执行to上下文 // 当执行完毕后,跳转到from上下文中 if(swapcontext(&from->uc, &to->uc) < 0){ fprint(2, "swapcontext failed: %r\n"); assert(0); } }
参考资料
libtask
解析
在libtask中,虽说是一个库,但是其包含了一个main函数,这和常规的库有点不一样,看看使用libtask的特点:
在注释中,给出了编译的方式,也就是说我们在利用libtask来做自己的应用程序的时候,需要自己从taskmain函数开始。
libtask中的main
该main函数的作用,调用taskmain作为第一个执行的coroutine,调度整个coroutine链。
main函数在执行完taskmain routine后,就开始执行taskscheduler,这个是整个libtask的核心,
taskscheduler
在这里我们搜索下taskschedcontext,这个变量:
跟踪一下该变量的使用,知道其是当前main coroutine的执行上下文,当从main coroutine中的调度器调度到别的coroutine上下文的时候,保存到taskschedcontext中(swapcontext中有有这个作用),这是个全局变量。
contextswitch
在linux中的uontext.h,提供的getcontext,setcontext,swapcontext和makecontext函数,具体的上下文有结构体ucontext_t指定,包括任务的栈的大小,栈顶指针以及cpu内寄存器信息。
我们看下contextswitch的实现