Home > programming > lthread代码分析(三)

lthread代码分析(三)

前面我们查看了lthread的创建以及调度器的运行代码,那么在不同的lthread中到底是如何切换的呢,我们现在来看一下lthread的切换代码lthread_resume函数。

int
_lthread_resume(lthread_t *lt)
{
    if (lt->state & bit(LT_NEW))/*如果是用lthread_create新创建的lthread,那么就初始化一下一些必要的数据*/
        _lthread_init(lt);

    _restore_exec_state(lt);/*这里是从lt中回复出当前的一些必要的执行环境数据,主要的是栈空间数据*/

    lthread_get_sched()->current_lthread = lt; /*设置调度器的当前运行lthread为lt*/
    _switch(&lt->st, &lt->sched->st);/*切换cpu执行代码位置,这里已经开始执行lt的代码了*/
    lthread_get_sched()->current_lthread = NULL;/*lt的代码执行完毕,或者是lt的执行函数中主动让出了执行权*/

    if (lt->state & bit(LT_EXITED)) {/*lthread执行完毕,做一些清理工作*/
        if (lt->lt_join) {
            /* if lthread was sleeping, deschedule it so that it doesn't expire. */
            _desched_lthread(lt->lt_join);
            LIST_INSERT_HEAD(&lthread_get_sched()->new, lt->lt_join, new_next);
            lt->lt_join = NULL;
        }

        /* if lthread is detached, free it, otherwise lthread_join() will */
        if (lt->state & bit(LT_DETACH))
            _lthread_free(lt);
        return -1;
    } else {
        _save_exec_state(lt);/*lt只会暂时交出了cpu的控制权,所以需要保存当前的运行环境,主要是栈空间数据*/
        /* place it in a compute scheduler if needed.  */
        if (lt->state & bit(LT_PENDING_RUNCOMPUTE)) {
            _lthread_compute_add(lt);
            lthread_get_sched()->sleeping_state++;
        }
    }

    return 0;
}

以上的部分就是如何运行一个lthread的代码,那么里面的函数saveexecstate和restoreexecstate分别是用来保存和回复运行环境的栈空间数据的。

inline int
_restore_exec_state(lthread_t *lt)
{
    if (lt->stack_size)
        memcpy(lt->st.esp, lt->stack, lt->stack_size);/*把保存下来的占空间数据copy到lt对应的sched的申请的栈空间*/

    return 0;
}

int
_save_exec_state(lthread_t *lt)
{
    void *stack_top = NULL;
    size_t size = 0;
    /*计算当前使用了多少占空间*/
    stack_top = lt->sched->stack + lt->sched->stack_size;
    size = stack_top - lt->st.esp;

    if (size && lt->stack_size != size) {
        if (lt->stack)
            free(lt->stack);
        if ((lt->stack = calloc(1, size)) == NULL) {
            perror("Failed to allocate memory to save stackn");
            abort();
        }
    }

    lt->stack_size = size;
    if (size)
        memcpy(lt->stack, lt->st.esp, size);/*把当前使用的栈上的数据保存下来,这样resume lthread的时候就可以使用了*/

    return 0;
}

这里我们可以看出,在lthread切换的时候的栈上的数据是直接保存在一个申请的额外空间里面的。最开始我也认为这样非常不好。但是如果不这样的话,就需要为每一个lthread申请一个很大的内存区域用作栈空间,这个明显是不现实的,特别是lthread的实例比较多的情况下。当然,现在这种处理方式也有一个问题。那就是lthread在执行的过程中使用了很多stack上的内存,例如char tmpBuffer[1024*1024]。这样会导致每次lthread切换的时候会有很多数据被copy过来,copy过去。如果在lthread的执行过程中需要一个大内存数据块,最好在堆上面分配内存,而不要使用栈上的内存。

Categories: programming Tags: ,
  1. No comments yet.
  1. No trackbacks yet.