文章标题

来源:互联网 发布:网游加速器 知乎 编辑:程序博客网 时间:2024/06/10 15:13

st的代码有一点难读, 主要是代理用了很多的宏实现, 这样gdb调试时断点不好设置, 而且库里面用到了一些偏底层的函数(对我来说, 很多函数都没有用到过), 比如mprotect, mmap之类的。 但是底层的原理还是比较简单的, 就是通过setjmp和longjmp实现缓存状态和切换。

apue上将goto语句不能跨越函数, 执行这类跳转功能的函数是setjmp和longjmp。
goto语句是在一个函数内实施跳转, 就是只能在当前的函数栈上跳转; 而setjmp和longjmp在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。 在希望返回的位置调用setjmp。setjmp的参数类型是一个特殊类型的jmp_buf. 这一数据类型是某种形式的数组, 其中存放在调用longjmp时能用来恢复栈状态的所有信息。

但是apue上也明确说了, 对于longjmp返回到setjmp的栈帧时,对于自动变量, 寄存器变量等能否回滚到原先值是不确定的。详细的请查看apue 上相关页.

直接通过例子分析st的流程:
看lookupdns程序:
st库的流程就是:
1. st_init()
2. st_thread_create()
3. st_thread_exit() 或 st_sleep

简单的流程:
1.0 st_init() 创建一个idle thread, _st_this_vp.idle_thread 指向该thread; 在调用st_thread_create的时候, 会设置setjmp点, 如果后面有longjmp了, 会执行传入st_thread_create的回调函数(这里传入的是 _st_idle_thread_start )

1.1 初始化一个primordial thread, 并设置全局变量_st_this_thread 指向该thread, 设置thread->state=_ST_ST_RUNNING

2.0 st_thread_create一个协程: 函数内部创建一个thread, 然后将其加入_st_this_vp.run_q链表

3.0 st_usleep一定时间的时候, 获取_st_this_thread指向的thread, 设置thread的状态为me->state = _ST_ST_SLEEPING, 将该thread加入sleep_q对列, 设置setjmp点 , 然后调用_st_vp_schedule时会取出run_q队列的thread, 然后longjmp该thread的context, 此时run_q只有2.0中创建的thread, 所以会跳到st_thread_create传入的回调函数中去执行.

详细分析代码:
st_init():

int st_init(void){  _st_thread_t *thread;   // 创建一个st_thread_t类型  if (_st_active_count) {  // _st_active_cout: global data, active thread count; st_init初始化后, 至少会有一个idle thread; 所以_st_active_cout 至少为1. 这里如果重复执行st_init会直接退出.    /* Already initialized */    return 0;  }  /* We can ignore return value here */  st_set_eventsys(ST_EVENTSYS_DEFAULT);  // 根据系统选择是调用epoll还是select, kqueue. centos下,是通过判断是否存在/usr/include/sys/epoll.h, 从而定义宏MD_HAVE_EPOLL, 参看Makefile:188行, 然后会根据该宏, 设置_st_eventsys = &_st_epoll_eventsys;, _st_eventsys也是一个全局变量, _st_epoll_eventsys里包含了一系列的回调函数  if (_st_io_init() < 0)  // ignore SIGPIPE信号, 并设置最大的文件描述符个数    return -1;  memset(&_st_this_vp, 0, sizeof(_st_vp_t)); // _st_this_vp 也是一个全局变量  ST_INIT_CLIST(&_ST_RUNQ);  // 初始化 _st_this_vp 的run_q链表  ST_INIT_CLIST(&_ST_IOQ);   // 初始化 _st_this_vp 的io_q链表  ST_INIT_CLIST(&_ST_ZOMBIEQ);    // 初始化 _st_this_vp的zombie_q#ifdef DEBUG  ST_INIT_CLIST(&_ST_THREADQ);    // 如果执行的是make linux-debug, 会定义DEBUG宏, 还会初始化_st_this_vp的thread_q#endif  if ((*_st_eventsys->init)() < 0)  // 我这里其实是调用_st_epoll_init    return -1;  _st_this_vp.pagesize = getpagesize();  // 获取分页的大小  _st_this_vp.last_clock = st_utime();   // 返回当前的时间,st_utime() 内部调用的是gettimeofday  /*   * Create idle thread   */  _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start,                         NULL, 0, 0);                         // st_thread_create函数讲解见下文  if (!_st_this_vp.idle_thread)    return -1;  _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD;  _st_active_count--;  _ST_DEL_RUNQ(_st_this_vp.idle_thread);  /*   * Initialize primordial thread   */  thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) +                   (ST_KEYS_MAX * sizeof(void *)));  if (!thread)    return -1;  thread->private_data = (void **) (thread + 1);  thread->state = _ST_ST_RUNNING;  thread->flags = _ST_FL_PRIMORDIAL;  _ST_SET_CURRENT_THREAD(thread);  _st_active_count++;#ifdef DEBUG  _ST_ADD_THREADQ(thread);#endif  return 0;}

st_thread_create

_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg,                   int joinable/*0*/, int stk_size/*0*/){  _st_thread_t *thread;  _st_stack_t *stack;  void **ptds;  char *sp;#ifdef __ia64__  char *bsp;#endif  /* Adjust stack size */  if (stk_size == 0)    stk_size = ST_DEFAULT_STACK_SIZE;   //   stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE;  stack = _st_stack_new(stk_size);  if (!stack)    return NULL;  /* Allocate thread object and per-thread data off the stack */#if defined (MD_STACK_GROWS_DOWN)  sp = stack->stk_top;#ifdef __ia64__  /*   * The stack segment is split in the middle. The upper half is used   * as backing store for the register stack which grows upward.   * The lower half is used for the traditional memory stack which   * grows downward. Both stacks start in the middle and grow outward   * from each other.   */  sp -= (stk_size >> 1);  bsp = sp;  /* Make register stack 64-byte aligned */  if ((unsigned long)bsp & 0x3f)    bsp = bsp + (0x40 - ((unsigned long)bsp & 0x3f));  stack->bsp = bsp + _ST_STACK_PAD_SIZE;#endif  sp = sp - (ST_KEYS_MAX * sizeof(void *));  ptds = (void **) sp;  sp = sp - sizeof(_st_thread_t);  thread = (_st_thread_t *) sp;  /* Make stack 64-byte aligned */  if ((unsigned long)sp & 0x3f)    sp = sp - ((unsigned long)sp & 0x3f);  stack->sp = sp - _ST_STACK_PAD_SIZE;#elif defined (MD_STACK_GROWS_UP)  sp = stack->stk_bottom;  thread = (_st_thread_t *) sp;  sp = sp + sizeof(_st_thread_t);  ptds = (void **) sp;  sp = sp + (ST_KEYS_MAX * sizeof(void *));  /* Make stack 64-byte aligned */  if ((unsigned long)sp & 0x3f)    sp = sp + (0x40 - ((unsigned long)sp & 0x3f));  stack->sp = sp + _ST_STACK_PAD_SIZE;#else#error Unknown OS#endif  memset(thread, 0, sizeof(_st_thread_t));  memset(ptds, 0, ST_KEYS_MAX * sizeof(void *));  /* Initialize thread */  thread->private_data = ptds;  thread->stack = stack;  thread->start = start;  thread->arg = arg;#ifndef __ia64__  // cpu的宏,   _ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main);#else  _ST_INIT_CONTEXT(thread, stack->sp, stack->bsp, _st_thread_main);#endif  /* If thread is joinable, allocate a termination condition variable */  if (joinable) {    thread->term = st_cond_new();    if (thread->term == NULL) {      _st_stack_free(thread->stack);      return NULL;    }  }  /* Make thread runnable */  thread->state = _ST_ST_RUNNABLE;  _st_active_count++;  _ST_ADD_RUNQ(thread);#ifdef DEBUG  _ST_ADD_THREADQ(thread);#endif  return thread;