文章标题

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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值