libtask协程库实现原理分析 (一)

Libtask is a simple coroutine library.  It runs on Linux (ARM, MIPS, and x86),FreeBSD (x86), OS X (PowerPC x86, and x86-64), and SunOS Solaris (Sparc),and is easy to port to other systems.

Libtask gives the programmer the illusion of threads, but the operating system sees only a single kernel thread.For clarity, we refer to the coroutines as "tasks," not threads.

(一)中主要分析协程正常执行调度流程。

libtask里面有两个关键函数taskcreate和taskscheduler

1. taskceate

调用taskcreate初始化一个新的task结构体,并插入到任务队列,设置状态为ready,准备被调度。taskcreate中传递function函数指针和参数,真正被调度时该函数将被执行。

int
taskcreate(void (*fn)(void*), void *arg, uint stack)
{
	int id;
	Task *t;

	t = taskalloc(fn, arg, stack);
	taskcount++;
	id = t->id;
	if(nalltask%64 == 0){
		alltask = realloc(alltask, (nalltask+64)*sizeof(alltask[0]));
		if(alltask == nil){
			fprint(2, "out of memory\n");
			abort();
		}
	}
	t->alltaskslot = nalltask;
	alltask[nalltask++] = t;
	taskready(t);
	return id;
}

在taskalloc中调用一个getcontext 和 makecontext.  makecontext修改通过getcontext取得的上下文ucp(这意味着调用makecontext前必须先调用getcontext),给该上下文指定一个栈空间ucp->stack,设置后继的上下文ucp->uc_link. (如果没有设置后继上下文的话,函数执行完毕将直接退出,libtask里面没有指定uc_link),除此之外,makecontext需传入func函数和所需参数。

当在taskscheduler函数中通过调用swapcontext or setcontext该上下文被激活时,makecontext传入的taskstart被执行。

        if(getcontext(&t->context.uc) < 0){
		fprint(2, "getcontext: %r\n");
		abort();
	}

	..........
	makecontext(&t->context.uc, (void(*)())taskstart, 2, y, x);

taskstart里面主要做了两件事,执行startfn(真正的任务,taskcreate时传入的handle function),调用taskexit将上下文切换到taskschedcontext

static void
taskstart(uint y, uint x)
{
	Task *t;
	ulong z;
	z = x<<16;	/* hide undefined 32-bit shift from 32-bit compilers */
	z <<= 16;
	z |= y;
	t = (Task*)z;
	t->startfn(t->startarg);
	taskexit(0);
}

2.taskscheduler

该函数一直循环从任务队列中读取任务t 。调用contextswitch保存当前上下文到taskschedcontext,切换上下文到task context  ,执行在任务创建时makecontext传入的taskstart函数。任务函数执行完毕后,主动切换回来,恢复当前上下文,进入下一轮循环。

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);
		contextswitch(&taskschedcontext, &t->context);
		taskrunning = nil;
		if(t->exiting){
			if(!t->system)
				taskcount--;
			i = t->alltaskslot;
			alltask[i] = alltask[--nalltask];
			alltask[i]->alltaskslot = i;
			free(t);
		}
	}
}

你可能感兴趣的:(libtask)