TOP

PHP 源码学习之线程安全(二)
2017-10-10 11:57:32 】 浏览:10140
Tags:PHP 源码 习之 线程 安全

ted_threads, int expected_resources, int debug_level, char *debug_filename) { /* code... */ tsrm_tls_table_size = expected_threads; // SAPI 初始化时预计分配的线程数,一般都为1 tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *)); /* code... */ id_count=0; resource_types_table_size = expected_resources; // SAPI 初始化时预先分配的资源表大小,一般也为1 resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type)); /* code... */ return 1; }

精简出其中完成的三个重要的工作,初始化了 tsrm_tls_table 链表、resource_types_table 数组,以及 id_count。而这三个全局变量是所有线程共享的,实现了线程间的内存管理的一致性。

资源 ID 的分配

我们知道初始化一个全局变量时需要使用 ZEND_INIT_MODULE_GLOBALS 宏(下面的数组扩展的例子中会有说明),而其实际则是调用的 ts_allocate_id 函数在多线程环境下申请一个全局变量,然后返回分配的资源 ID。代码虽然比较多,实际还是比较清晰,下面附带注解进行说明:

TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
{
    int i;

    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size));

    // 加上多线程互斥锁
    tsrm_mutex_lock(tsmm_mutex);

    /* obtain a resource id */
    *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); // 全局静态变量 id_count 加 1
    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));

    /* store the new resource type in the resource sizes table */
    // 因为 resource_types_table_size 是有初始值的(expected_resources),所以不一定每次都要扩充内存
    if (resource_types_table_size < id_count) {
        resource_types_table = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
        if (!resource_types_table) {
            tsrm_mutex_unlock(tsmm_mutex);
            TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
            *rsrc_id = 0;
            return 0;
        }
        resource_types_table_size = id_count;
    }

    // 将全局变量结构体的大小、构造函数和析构函数都存入 tsrm_resource_type 的数组 resource_types_table 中
    resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
    resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
    resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
    resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;

    /* enlarge the arrays for the already active threads */
    // PHP内核会接着遍历所有线程为每一个线程的 tsrm_tls_entry
    for (i=0; i<tsrm_tls_table_size; i++) {
        tsrm_tls_entry *p = tsrm_tls_table[i];

        while (p) {
            if (p->count < id_count) {
                int j;

                p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
                for (j=p->count; j<id_count; j++) {
                    // 在该线程中为全局变量分配需要的内存空间
                    p->storage[j] = (void *) malloc(resource_types_table[j].size);
                    if (resource_types_table[j].ctor) {
                        // 最后对 p->storage[j] 地址存放的全局变量进行初始化,
                        // 这里 ts_allocate_ctor 函数的第二个参数不知道为什么预留,整个项目中实际都未用到过,对比PHP7发现第二个参数也的确已经移除了
                        resource_types_table[j].ctor(p->storage[j], &p->storage);
                    }
                }
                p->count = id_count;
            }
            p = p->next;
        }
    }

    // 取消线程互斥锁
    tsrm_mutex_unlock(tsmm_mutex);

    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
    return *rsrc_id;
}

当通过 ts_allocate_id 函数分配全局资源 ID 时,PHP 内核会先加上互斥锁,确保生成的资源 ID 的唯一,这里锁的作用是在时间维度将并发的内容变成串行,因为并发的根本问题就是时间的问题。当加锁以后,id_count 自增,生成一个资源 ID,生成资源 ID 后,就会给当前资源 ID 分配存储的位置, 每一个资源都会存储在 resource_types_ta
PHP 源码学习之线程安全(二) https://www.cppentry.com/bencandy.php?fid=85&id=129772

首页 上一页 1 2 3 4 5 下一页 尾页 2/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇PHP mysqli_free_result()与mysql.. 下一篇WNMP集成环境下配置thinkPHP