今天开始探讨st库里的线程调用模型
1)idle_thread;
st库的核心是idle_thread,整个库中,只有调用了底层的分发器dispatch,所有的i/o动作,所有的sleep,都必须汇总到这个函数中来。
首先看初始化伪代码
st_ini()
{
....
new idle_thread;
int thread stack;
if(setjmp(idle->context))//第一次设置的时候,并不会执行下面的部分。
{
while(st_active>0)//这个值,在每次调用st_sleep() st_read() st_write()等函数的时,都会+1
{
st_poll_dispatch();//调用底层的epoll等函数,把合适的i/o和时间处理函数放到runq队列中
st_sleep_deal();//检测时间队列中是否有到时的
st_state =RUNABLE;//
if(!setjmp(idle_thread->context))//再次跳转,注意这里的context变化了
{
find_thread_from_runq()
else
set_thread_idle() //这一个if_else,做一个可运行的线程选择。
longjmp(thread_context);//到最后可以执行的线程中去。
}
}
}
}
上面的代码是我把所有的宏都展开以后的代码。可以看出
1)在idle线程刚创建的时候,是不会被执行的。
2)只有外部调用了驱动函数,比如st_sleep,st_read等驱动函数时,才会执行idle_thread.
3)idle_thread线程的的函数context会变化。首次是在ilde_thread_start的头部,之后就在while循环里面。
几个比较重要的调度函数
调度宏
#define _ST_SWITCH_CONTEXT(_thread) \
ST_BEGIN_MACRO \
ST_SWITCH_OUT_CB(_thread); \
if (!MD_SETJMP((_thread)->context)) { \
_st_vp_schedule(); \
} \
ST_DEBUG_ITERATE_THREADS(); \
ST_SWITCH_IN_CB(_thread); \
ST_END_MACRO
这个宏的特色之处就在,setjmp之后,他会直接跳进_st_vp_schedule()函数里面。st_sleep st_read st_write等函数,都是其针对不同业务的封装。
整个调度过程
外部必须不断的调度st_sleep() st_read() st_write(),因为这些都是驱动力,在调用了这些之后,库会把这些线程放到ioq timeq 或者runq等几个队列中,然后重新保存这些函数的context,调用vp_schduel函数,最终会调用到idle_thread的执行函数执行epoll_wait,如此不断的循环。
终于搞通了。
那么最后一个结论
如果ilde_thead的while()循环退出了。那么正st库也就完蛋了