文章标题
来源:互联网 发布:网游加速器 知乎 编辑:程序博客网 时间: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;
- 文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题 文章标题 文章标题 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 几个基础算法介绍和实现——希尔排序
- 欢迎使用CSDN-markdown编辑器
- Servlet中的过滤器Filter详解
- 关于malloc/free && new/delete
- c++基础和《C++ Primer第五版中文版》笔记
- 文章标题
- 了解kafka
- testng.xml文件结构
- java多线程(一)
- 工厂方法模式案例
- php通过Qrcode生成二维码
- ubuntu安装apache
- 微信小程序
- open-falcon资料汇总