PHP源码分析之线程安全模型

小波变换在EEG信号处理中的时频定位原理与实战 小波变换是一种面向非平稳信号的时频分析方法,通过可缩放平移的基函数实现时间与频率的联合分辨,克服了傅里叶变换缺乏时间定位能力的根本缺陷。其核心价值在于为脑电(EEG)这类高度动态、瞬态显著的神经信号提供毫秒级+频带级的双重解析能力,支撑癫痫放电定位、事件相关电位检测、睡眠分期等关键应用。在工程实践中,Morlet小波因优异的时频平衡性与复数相位提取能力,成为EEG分析首选;而尺度参数映射、边缘效应抑制、多重比较校正等细节,直接决定临床 biomarker 的可靠性。本文围绕EEG信号处理与小波变换两大热词, 阅读详情

欢迎转载,转载请注明出处http://blog.youkuaiyun.com/hackooo/article/details/8702156 谢谢!新浪微博:小灰马

     0.前言  

      相信很多人跟我一样,一开始看PHP源码的时候看到一堆的TSRM_CC,TSRM_DC特别蛋疼,大多数函数的声明都会在参数末尾加个TSRM_DC,着实让像我这样以前没搞过多线程编程的很不理解。网上找了找,介绍这方面的材料非常少,只找到@张洋的一篇文章,《PHP及Zend Engine的线程安全模型》 。不过看了他的文章,看完还是有很多疑问,他的文章也没仔细说清楚,那就只能自己看源码啦!先是去看了《多线程程序设计》这本书,然后再扫一扫PHP的线程安全相关的源码,算是有点眉目,对@张洋的文章里面提的一些问题也有了自己一些见解。以下是个人一些拙见,若有不妥的地方,欢迎交流讨论,谢谢~

      1.线程私有数据

      我们知道,在多线程环境中,线程之间是共享内存的地址空间的,那么保证线程之间的各自的私有数据读写不相互干扰就显得非常重要。那么这个需求如何实现呢?有个非常简单的办法(当然还有更好的实现),举个例子,

      假如现在有三个线程 thread1,thread2,thread3,

      我们声明一个简单的全局的数组变量,不妨叫 global_arr,

      那么各个线程每次读写自己的数据的时候提供一个自己的线程id,到global_arr里面取不就得了,global_arr[0],global_arr[1] ... ,非常简单吧...

      不过显然这样做是不太完善的,global_arr是个全局变量,暴露给了所有的线程,要是某个thread由于种种原因,提供一个错误的id去global_arr里面取数据,那结果肯定是很严重的。

      不同的系统环境有多种不同的多线程的实现,以pthread为例,它提供了以下几个相关的结构和api:

      

pthread_key_t key;                                                        //线程的一个key
int pthread_key_create(pthread_key *key,void(*destructor)(void *));       //初始化一个key
int pthread_key_delete(pthread_key_t key);                                //删除一个key
int pthread_setspecific(pthread_key_t key,const void *value);             //为一个key指定value
void *pthread_getspecific(pthread_key_t key);                             //获取一个key指向的value

这样,可以把pthead对线程私有数据实现理解为:提供一个简单的key-value实现,而且各个线程不相互干扰。可以在线程初始化的时候声明一个key,这里的key指向一个大一点的数据结构,线程把要保存的一堆私有数据全丢里面去,以后线程就用一个key把这个结构找到,然后再在里面找具体的item,这样就不用说每次要保存一个小的私有数据就声明一个key,因为这样太浪费资源了。

      2.PHP中的线程安全实现

      2.1总体结构:

      tsrm_tls_table (意思应该是tsrm thread local storage table)是个哈希表,我们姑且称之为“全局线程私有数据表”,存放的是每个线程私有数据的指针。

      

      这个哈希表的算法非常简单,就是最常用的取余数法,解决哈希冲突的方法采用了拉链法。

# /PHP_5_4/TSRM/TSRM.h  //thr是线程的thread_id,ts是tsrm_tls_table_size,即全局线程私有数据表的大小。
100#define THREAD_HASH_OF(thr,ts)  (unsigned long)thr%(unsigned long)ts

      tsrm_tls_entry就是每个线程私有数据的入口啦,从名字就能看出来:tsrm thread local storage entry。

# /PHP_5_4/TSRM/TSRM.c
typedef struct _tsrm_tls_entry tsrm_tls_entry;

struct _tsrm_tls_entry {
    void **storage;        //存放一个一个resource的指针
    int count;             //多少个resouce
    THREAD_T thread_id;    //线程的id
    tsrm_tls_entry *next;  //下个entry的指针
};

      第一节提到的线程去取自己的私有数据是使用一个key去取的,这里key指向的value就是一个tsrm_tls_entry的指针,也就是说每个线程每次取自己的私有数据的时候,是通过一个key,从tsrm_tls_table取到属于自己的tsrm_tls_entry的结构。

      PHP中的tsrm_tls_entry存放的是一些resource,每个线程都有一些功能一样的resource,例如,都需要个zend_executor_globals结构,用来存放一些运行时的“全局变量”,但是每个线程的运行环境不一样,使用的zend_executor_globals肯定不能是同一个吧,因此实际上,每个线程都有这些resource的一份拷贝。

      我们看下tsrm_resource_type的结构:

typedef struct {
    size_t size;            //所占内存大小
    ts_allocate_ctor ctor;  //constructor
    ts_allocate_dtor dtor;  //destructor
    int done;               //是否已经结束利用,释放掉了
} tsrm_resource_type;
     可以看到,TSRM中对资源的描述非常简单,一个描述这块内存大小,一个构造函数,一个析构函数,还有一个标志位标识使用情况。

     上面的tsrm_tls_entry通过storage成员指向一个一个实际的resource:

static tsrm_resource_type   *resource_types_table=NULL;
static int                  resource_types_table_size;


     

      按照上图那样,tsrm_tls_entry的storage存放的是一些资源的指针,resource_pointer指向的实际的内存块是依据resource_type_table一个个资源描述结构 构造出来的。这样看,resource_type_table是不是像一个模具,然后线程就依据这个模具造出一个个私有数据出来呢?举个形象的例子,这个resource_type_table就像一个存放房屋工程设计图的本本,里面存放一张张工程设计的图纸,图纸内容分为四类:1.要建造的房子的大小;2.房子怎么建造;3.房子旧了怎么拆掉;4.这张图纸是否过时了。而每一个线程像一个个房屋建造的团队,它们在全国各地根据一张张的图纸,把一个个房子给建起来。以后就把房子卖给不同的人群做不同的事(租地下室给码农啦,投资增值啦...尼玛不说了)。

      2.2相关的API及算法:

      首先把大体的api列一列:

/* Startup TSRM (call once for the entire process) TSRM启动函数,整个生命周期只执行一遍,前两个参数:1.要预启动多少个线程2.要预分配多少个资源*/
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename)

/* Shutdown TSRM (call once for the entire process) TSRM关闭函数,整个生命周期只执行一遍*/
TSRM_API void tsrm_shutdown(void)

/* allocates a new thread-safe-resource id ,往resource_type_table里添加一种资源类型,各个线程按这个类型分配一块内存块,返回这个资源类型的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)

/* 生成一个新的线程私有数据结构,并按照resource_type_table把资源分配给它 */
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)

/* fetches the requested resource for the current thread 根据资源的id返回指定线程拥有的的资源*/
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)

/* 下面这几个跟上下文相关的api是给那些对整个线程安全模型了如指掌的开发人员用的。
 * frees an interpreter context.  You are responsible for making sure that
 * it is not linked into the TSRM hash, and not marked as the current interpreter 
 * 释放一个上下文,你必须确保它没链到全局的tsrm_tls_table以及没有把它当做当前线程的上下文。*/
void tsrm_free_interpreter_context(void *context)
void *tsrm_set_interpreter_context(void *new_ctx) /* 设置当前线程的上下文,并把老的上下文返回*/
void *tsrm_new_interpreter_context(void)          /* 设置一个新的上下文,用到上面的tsrm_set_interpreter_context */
void ts_free_thread(void)                         /* 把当前的线程的所占的resource都释放掉。*/
void ts_free_worker_threads(void)                 /* 把除了当前线程的其它线程的resource都释放掉*/
void ts_free_id(ts_rsrc_id id)                    /* 把resource_type_table里面指定资源id的资源(包括所有线程中,资源id为此id的资源)全释放掉,并标记done */

/*下面是一些简单的工具*/
TSRM_API THREAD_T tsrm_thread_id(void)          /* 获得当前线程的id */
TSRM_API MUTEX_T tsrm_mutex_alloc(void)         /* 分配一个锁*/
TSRM_API void tsrm_mutex_free(MUTEX_T mutexp)   /* 删除锁*/
TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp)    /* 加锁 */
TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp)  /* 解锁*/
TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset)  /* 信号相关 */
/*设置线程初始句柄*/
TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler)  
/*设置线程结束句柄*/
TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler)
/*DEBUG支持*/
int tsrm_error(int level, const char *format, ...)
void tsrm_error_set(int level, char *debug_filename)

      下面结合源码一个一个看TSRM怎么实现的:

/* Startup TSRM (call once for the entire process) */
/* tsrm启动函数 */
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename)
{
#if defined(GNUPTH)
    pth_init();
#elif defined(PTHREADS)
    pthread_key_create( &tls_key, 0 );       //初始化线程的tls_key
#elif defined(TSRM_ST)
    st_init();
    st_key_create(&tls_key, 0);
#elif defined(TSRM_WIN32)
    tls_key = TlsAlloc();
#elif defined(BETHREADS)
    tls_key = tls_allocate();
#endif

    tsrm_error_file = stderr;
    tsrm_error_set(debug_level, debug_filename);
    //初始化全局线程私有数据表
    tsrm_tls_table_size = expected_threads;
    tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *));
    if (!tsrm_tls_table) {
        TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate TLS table"));
        return 0;
    }
    //初始化全局资源表,id_count是一个计数器的全局变量
    id_count=0;

    resource_types_table_size = expected_resources;
    resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type));
    if (!resource_types_table) {
        TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate resource types table"));
        free(tsrm_tls_table);
        tsrm_tls_table = NULL;
        return 0;
    }
    //初始化锁
    tsmm_mutex = tsrm_mutex_alloc();
    //初始化线程的开始和结束句柄
    tsrm_new_thread_begin_handler = tsrm_new_thread_end_handler = NULL;

    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected \
    threads, %d expected resources", expected_threads, expected_resources));
    return 1;
}


/* Shutdown TSRM (call once for the entire process) TSRM结束函数,做一些内存清理工作*/
TSRM_API void tsrm_shutdown(void)
{
    int i;
    //把tsrm_tls_table所有线程的私有数据全部干掉
    if (tsrm_tls_table) {
        for (i=0; i<tsrm_tls_table_size; i++) {
            tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;

            while (p) {
                int j;

                next_p = p->next;
                for (j=0; j<p->count; j++) {
                    if (p->storage[j]) {
                        if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) {
                            resource_types_table[j].dtor(p->storage[j], &p->storage);
                        }
                        free(p->storage[j]);
                    }
                }
                free(p->storage);
                free(p);
                p = next_p;
            }
        }
        free(tsrm_tls_table);
        tsrm_tls_table = NULL;
    }
    //释放资源表
    if (resource_types_table) {
        free(resource_types_table);
        resource_types_table=NULL;
    }
    //释放锁
    tsrm_mutex_free(tsmm_mutex);
    tsmm_mutex = NULL;
    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM"));
    if (tsrm_error_file!=stderr) {
        fclose(tsrm_error_file);
    }
    //删除线程的key
#if defined(GNUPTH)
    pth_kill();
#elif defined(PTHREADS)
    pthread_setspecific(tls_key, 0);
    pthread_key_delete(tls_key);
#elif defined(TSRM_WIN32)
    TlsFree(tls_key);
#endif
}


/* 往resource_type_table里添加一种资源类型,各个线程按这个类型分配一块内存块,返回这个资源类型的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);

    /* 获得一个资源的id ,这里为啥要用到TSRM_SHUFFLE_RSRC_ID呢,目的是为了与resource_types_table_size对应,
    *  resource_types_table[0] 存放第一个资源,rsrc_id = id_count = 1 ,resource_types_table_size = 1;
    *  resource_types_table[1] 存放第二个资源,rsrc_id = id_count = 2 ,resource_types_table_size = 2;
    *  resource_types_table[2] 存放第三个资源,rsrc_id = id_count = 3 ,resource_types_table_size = 3;
    *  实际上,根据id_count的名称,作者的本意应该是用这个变量来计数有多少个资源的id的,而每个线程的tsrm_tls_entry也有个count成员,与这个是对应的。
    *  这样 id_count 与 resource_types_table_size就能进行直接比较,而且保证返回的rsrc_id大于0,
    *  只不过在实际保存到resource_types_table里的时候,要把id unshuffle一下,也就是减一操作,因为索引是从0开始的。
    */
    *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
    /* 全局资源表扩容,添加一个新的资源类型*/
    /* store the new resource type in the resource sizes table */
    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;
    }
    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;
    /* 把所有已经active的线程的资源根据id_count扩容,
     * 也就是说,假如入原来线程只保存了1~3三个资源,现在id_count如果经过一些操作,增加到5个了,那么线程必须把4和5两个资源给补齐。*/
    /* enlarge the arrays for the already active threads */
    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) {
                        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;
}

/* 生成一个新的线程私有数据结构,并按照resource_type_table把资源分配给它 
*  注意,首先扫下这个函数,并没有加锁操作,只有解锁操作,所以,使用这个函数前应首先加锁 tsmm_mutex
*  后面的ts_resource_ex确实就是这么干的。
*/
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
{
    int i;

    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id));
    /*生成一个线程的私有数据结构,叫资源入口吧*/
    (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry));
    (*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
    (*thread_resources_ptr)->count = id_count;
    (*thread_resources_ptr)->thread_id = thread_id;
    (*thread_resources_ptr)->next = NULL;

    /* Set thread local storage to this new thread resources structure */
    /把当前线程的key指向这个资源入口/
    tsrm_tls_set(*thread_resources_ptr);

    if (tsrm_new_thread_begin_handler) {
        tsrm_new_thread_begin_handler(thread_id, &((*thread_resources_ptr)->storage));
    }
    /* 按照resrouce_types_table 把所有资源给拷贝一份,那些done的不拷。*/
    for (i=0; i<id_count; i++) {
        if (resource_types_table[i].done) {
            (*thread_resources_ptr)->storage[i] = NULL;
        } else
        {
            (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
            if (resource_types_table[i].ctor) {
                resource_types_table[i].ctor((*thread_resources_ptr)->storage[i], &(*thread_resources_ptr)->storage);
            }
        }
    }

    if (tsrm_new_thread_end_handler) {
        tsrm_new_thread_end_handler(thread_id, &((*thread_resources_ptr)->storage));
    }

    tsrm_mutex_unlock(tsmm_mutex);
}


/* 根据资源的id返回指定线程拥有的的资源 */
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
{
    THREAD_T thread_id;
    int hash_value;
    tsrm_tls_entry *thread_resources;
    /* 这个 NETWART 表示还没搞清楚什么情况下回触发,待研究,mark一下========================================*/
#ifdef NETWARE
    /* The below if loop is added for NetWare to fix an abend while unloading PHP
     * when an Apache unload command is issued on the system console.
     * While exiting from PHP, at the end for some reason, this function is called
     * with tsrm_tls_table = NULL. When this happened, the server abends when
     * tsrm_tls_table is accessed since it is NULL.
     */
    if(tsrm_tls_table) {
#endif
    /* 如果没传线程的id进来,就默认是访问当前线程 */
    if (!th_id) {
        /* Fast path for looking up the resources for the current
         * thread. Its used by just about every call to
         * ts_resource_ex(). This avoids the need for a mutex lock
         * and our hashtable lookup.
         */
        /* 快速获取线程的私有数据的指针,放在key里面 ,防止每次都去tsrm_tls_table里面找一遍,因为这个table是个哈希表,找起来还是蛮消耗资源的*/
        thread_resources = tsrm_tls_get();

        if (thread_resources) {
            TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d \
            for current thread %d", id, (long) thread_resources->thread_id));
            /* Read a specific resource from the thread's resources.
             * This is called outside of a mutex, so have to be aware about external
             * changes to the structure as we read it.
             */
            TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
        }
        thread_id = tsrm_thread_id();
    } else {
        thread_id = *th_id;
    }
    /* 在线程的key里面没找着,原因可能是还没为这个线程分配资源呢!*/
    TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id));
    /* 加锁 */
    tsrm_mutex_lock(tsmm_mutex);

    hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
    thread_resources = tsrm_tls_table[hash_value];

    if (!thread_resources) {/* 还没为这个线程分配资源,分配之~*/
        allocate_new_resource(&tsrm_tls_table[hash_value], thread_id);
        return ts_resource_ex(id, &thread_id);
    } else {/* 在同一个hash值的单链表里,找之~ 如果这个链表里也没有,分配之~*/
         do {
            if (thread_resources->thread_id == thread_id) {
                break;
            }
            if (thread_resources->next) {
                thread_resources = thread_resources->next;
            } else {
                allocate_new_resource(&thread_resources->next, thread_id);
                return ts_resource_ex(id, &thread_id);
                /*
                 * thread_resources = thread_resources->next;
                 * break;
                 */
            }
         } while (thread_resources);
    }
    tsrm_mutex_unlock(tsmm_mutex);
    /* Read a specific resource from the thread's resources.
     * This is called outside of a mutex, so have to be aware about external
     * changes to the structure as we read it.
     */
    TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
#ifdef NETWARE
    }   /* if(tsrm_tls_table) */
#endif
}

/* frees an interpreter context.  You are responsible for making sure that
 * it is not linked into the TSRM hash, and not marked as the current interpreter */
/* 把一个线程的资源上下文释放掉,当然,必须保证它没用了,也就是即不是当前线程的上下文,也不是别的线程的上下文。 */
void tsrm_free_interpreter_context(void *context)
{
    tsrm_tls_entry *next, *thread_resources = (tsrm_tls_entry*)context;
    int i;

    while (thread_resources) {
        next = thread_resources->next;

        for (i=0; i<thread_resources->count; i++) {
            if (resource_types_table[i].dtor) {
                resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage);
            }
        }
        for (i=0; i<thread_resources->count; i++) {
            free(thread_resources->storage[i]);
        }
        free(thread_resources->storage);
        free(thread_resources);
        thread_resources = next;
    }
}
/* 为当前线程设置一个新的上下文,并把老的上下文的指针返回 */
void *tsrm_set_interpreter_context(void *new_ctx)
{
    tsrm_tls_entry *current;

    current = tsrm_tls_get();

    /* TODO: unlink current from the global linked list, and replace it
     * it with the new context, protected by mutex where/if appropriate */

    /* Set thread local storage to this new thread resources structure */
    tsrm_tls_set(new_ctx);

    /* return old context, so caller can restore it when they're done */
    return current;
}


/* 这个函数相当于用上面的函数生成一个新的上下文,
*  然后当前线程切换回原来的上下文,返回这个新的上下文的指针,相当于拷贝了一份自己的孪生兄弟出来。 */
void *tsrm_new_interpreter_context(void)
{
    tsrm_tls_entry *new_ctx, *current;
    THREAD_T thread_id;

    thread_id = tsrm_thread_id();
    tsrm_mutex_lock(tsmm_mutex);

    current = tsrm_tls_get();

    allocate_new_resource(&new_ctx, thread_id);

    /* switch back to the context that was in use prior to our creation
     * of the new one */
    return tsrm_set_interpreter_context(current);
}


/* 把当前线程的资源都释放掉 */
void ts_free_thread(void)
{
    tsrm_tls_entry *thread_resources;
    int i;
    THREAD_T thread_id = tsrm_thread_id();
    int hash_value;
    tsrm_tls_entry *last=NULL;

    tsrm_mutex_lock(tsmm_mutex);
    hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
    thread_resources = tsrm_tls_table[hash_value];

    while (thread_resources) {
        if (thread_resources->thread_id == thread_id) {
            for (i=0; i<thread_resources->count; i++) {
                if (resource_types_table[i].dtor) {
                    resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage);
                }
            }
            for (i=0; i<thread_resources->count; i++) {
                free(thread_resources->storage[i]);
            }
            free(thread_resources->storage);
            if (last) {
                last->next = thread_resources->next;
            } else {
                tsrm_tls_table[hash_value] = thread_resources->next;
            }
            tsrm_tls_set(0);
            free(thread_resources);
            break;
        }
        if (thread_resources->next) {
            last = thread_resources;
        }
        thread_resources = thread_resources->next;
    }
    tsrm_mutex_unlock(tsmm_mutex);
}


/* 把除当前线程外的其它线程的资源都释放掉 */
void ts_free_worker_threads(void)
{
    tsrm_tls_entry *thread_resources;
    int i;
    THREAD_T thread_id = tsrm_thread_id();
    int hash_value;
    tsrm_tls_entry *last=NULL;

    tsrm_mutex_lock(tsmm_mutex);
    hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
    thread_resources = tsrm_tls_table[hash_value];

    while (thread_resources) {
        if (thread_resources->thread_id != thread_id) {
            for (i=0; i<thread_resources->count; i++) {
                if (resource_types_table[i].dtor) {
                    resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage);
                }
            }
            for (i=0; i<thread_resources->count; i++) {
                free(thread_resources->storage[i]);
            }
            free(thread_resources->storage);
            if (last) {
                last->next = thread_resources->next;
            } else {
                tsrm_tls_table[hash_value] = thread_resources->next;
            }
            free(thread_resources);
            if (last) {
                thread_resources = last->next;
            } else {
                thread_resources = tsrm_tls_table[hash_value];
            }
        } else {
            if (thread_resources->next) {
                last = thread_resources;
            }
            thread_resources = thread_resources->next;
        }
    }
    tsrm_mutex_unlock(tsmm_mutex);
}


/* 干掉一个资源,并在全局资源表中标记为done */
void ts_free_id(ts_rsrc_id id)
{
    int i;
    int j = TSRM_UNSHUFFLE_RSRC_ID(id);

    tsrm_mutex_lock(tsmm_mutex);

    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Freeing resource id %d", id));

    if (tsrm_tls_table) {
        for (i=0; i<tsrm_tls_table_size; i++) {
            tsrm_tls_entry *p = tsrm_tls_table[i];

            while (p) {
                if (p->count > j && p->storage[j]) {
                    if (resource_types_table && resource_types_table[j].dtor) {
                        resource_types_table[j].dtor(p->storage[j], &p->storage);
                    }
                    free(p->storage[j]);
                    p->storage[j] = NULL;
                }
                p = p->next;
            }
        }
    }
    resource_types_table[j].done = 1;

    tsrm_mutex_unlock(tsmm_mutex);

    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully freed resource id %d", id));
}






欢迎转载,转载请注明出处http://blog.youkuaiyun.com/hackooo/article/details/8702156 谢谢!新浪微博:小灰马

PHP及Zend Engine的线程安全模型分析 不知道怎么回事总是令人不舒服的,因此我通过阅读源码和查阅有限的资料简要了解一下相关机制,本文是我对研究内容的总结。 本文首先解释了线程安全的概念及PHP中线程安全的背景,然后详细研究了PHP的线程安全机制ZTS(Zend Thread Safety)及具体的实现TSRM,研究内容包括相关数据结构、实现细节及运行机制,最后研究了Zend对于单线程和多线程环境的选择性编译问题。 线程安全 线程安全问题,一言以蔽之就是多线程环境下如何安全存取公共资源。我们知道,每个线程只拥有一个私有栈,共享所属进程的堆。在C中,当一个变量被声明在任何函数之外时,就成为一个全局变量,这时这个变量会被分配到进程的共享存 立即下载

相关推荐

Mathcad在LLC谐振变换器设计中的公式推导实战

本文通过Mathcad软件,详细演示了LLC谐振变换器设计的核心公式推导实战。文章阐述了如何利用Mathcad作为“会思考的草稿纸”,从定义基本参数、应用基波近似法(FHA)模型,到逐步推导电压增益公式及确定工作边界频率(fmin, fmax),并展示了如何通过动态参数扫描进行优化设计,帮助电源工程师高效、直观地完成理论计算,为后续仿真与实现奠定坚实基础。

q1w3e5r7t9y的博客 293

php多线程模型,PHP进程模型、进程通讯方式、进程线程的区别分别有哪些?

PHP进程模型是一个正在执行的程序,可以分配给处理器并由处理器执行的一个实体;PHP进程通讯方式有管道及有名管道,信号,共享内存等;PHP进程线程的区别有进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元等。PHP进程模型、进程通讯方式、进程线程的区别分别是:一、PHP进程模型进程的概念是操作系统的结构的基础。Multics的设计者在20世纪60年代首次使用了这个技术词语,它比作业...

weixin_39571179的博客 236

树莓派5首次开机保姆级教程(无显示器通过VNC连接树莓派桌面)

选择 Interface Options > VNC > Yes 启用服务。2.在Windows电脑上安装VNC软件。重启树莓派:sudo reboot。3.使用VNC远程访问树莓派桌面。

qq_34184505的博客 2349

多线程编程 - PHP 实现

前言 前些天帮同事查一个问题,第一次接触到了 PHP 的多线程,原以为 PHP 普遍都是单线程模型,并不适合多线程领域,花些时间翻了几个多线程的项目源码之后,发现 PHP 的多线程也颇有可取之处,活用起来,用来解决某些问题竟然非常适合。 于是找了几篇文章看了下 PHP 多线程 TSRM 机制的实现,也有所收获,详情可以查看下面的参考文章。本文对比多进程介绍了下多线程的优势和适用场景,提出了...

weixin_34210740的博客 1183

Flowable任务节点动态预测:基于任务ID与参数获取下一节点及候选分配策略

本文深入探讨了Flowable工作流引擎中任务节点动态预测功能的实现。通过解析任务ID与运行时参数,模拟流程执行路径,精准预测下一节点及候选用户/组集合,从而提升用户体验与系统透明度。核心在于利用自定义命令实现表达式求值,并深度解析候选者分配策略,确保预测结果与实际流程执行一致。

nnn11的博客 736

因果表示学习 (Causal Representation Learning, CRL)

传统的机器学习,尤其是深度学习,非常擅长学习数据中的相关性 (Correlation)。但它常常会走“捷径”,学到一些虚假的、不稳定的“表面联系”。一个AI模型在学习识别“牛”时,如果训练数据里所有的牛都在绿色的草地上,模型很可能会学到一个错误的规则:“牛 = 四条腿的生物 + 绿色背景”。你给它一张牛在海滩上的照片,它就可能不认识了。因果表示学习的目标,就是让模型超越这种表面相关性,去学习数据背后。

Xuxiansir的博客 1180

PHP 线程,进程、并发、并行 的理解

摘录自(有删减和修改): http://chenpeng.info/html/3021 进程是操作系统分配资源和调度的功能单元,一个进程里面有一个或者多个线程,线程是操作系统的一个独立运算单元。通常可以认为一个软件对应一个进程,并行是指多个软件一起运行,实际上分时复用而已,并发是指某个软件在运行的时候把计算分给多个CPU同时进行计算,是真正的同一时间内一起发生的事情。 一个比较形象的理解方式:

zoujunjie202的博客 2万+

SOME/IP协议详解「2.1.2·Payload数据类型序列化」

SOME/IP协议详解「2.1.2·Payload数据类型序列化」 点击返回雪云飞星的SOME/IP协议详解「总目录」 SOME/IP协议详解「2.1.2·Payload数据类型序列化」1 大小端问题2 数据对齐填充问题3 都有哪些数据类型 之前简单的概括了一下序列化/反序列化,就是降维打击,从这一章开始到2.1所有章节结束,都在讲解这个降维打击的详细规则与原理 1 大小端问题 首先什么是大小端: 大端:高字节存放到内存的低地址 小端:高字节存放到内存的高地址 假如有一个数据是0x12345678

雪云飞星的博客 4624

PHP里的多线程(pthreads)

译者注:前段子打算用到PHP的多线程, 但搜了一下资料, 才发现PHP多线程的资料少之又少, PHP官方文档里有讲到多线程,但是讲太少了,连多线程在PHP中是怎么运行的也没有说, 所以说要真正能够使用多线程的功能还有很多的概念需要明白,有很多的资料需要整理, 也正如前面的译文那样, PHP的多线程还是跟其它语言的多线程使用方法有很多的区别的,所以其它语言的那一段也并不完全适用。上Github找到了这

AwayIsBlue 5737

YOLOv10改进策略【注意力机制篇】| 2024 蒙特卡罗注意力(MCAttn)模块,提高小目标的关注度

本文记录的是基于蒙特卡罗注意力(MCAttn)模块的YOLOv10目标检测改进方法研究。利用提高的跨尺度特征提取能力,使模型能够更好地传递和融合提取的多尺度特征,提高对小目标的关注度。Exploiting Scale-Variant Attention for Segmenting Small Medical Objects使用一种基于随机采样的池化操作来生成尺度无关的注意力图。它从三个不同尺度(3×33×33×3、2×22×22×2和1×11×11×1,即池化张量)中随机选择一个1×11×11×1注意力图

Limiiiing的博客 2185

QGIS003:【02标注工具栏】-标注的样式、放置和属性修改

摘要:文本介绍了QGIS标注的样式、放置和属性修改

qq_31762031的博客 1916

在Ubuntu 14.04 64bit上编译并研究State Threads网络线程库源码

State Threads是一个广受关注的高性能网络线程库,winlin在SRS中做了比较充分的应用,我很遗憾直到现在才精心研究它。下面是我的研究实录,以作备忘。 一、源码编译 下面是在Ubuntu 14.04 64bit上面的实操记录: 从官网http://sourceforge.net/projects/state-threads/下载源码包,最新版是1.9,如果不能下载,就从githu

雪峰流云 3248

【实战】LLaMA-Factory 如何做 LLM 的多轮对话微调

LLaMA-Factory 作为一个广受欢迎、易于使用的 LLM 微调框架,为开发者提供了便捷的途径来针对特定任务(包括多轮对话)优化模型。本文将深入探讨 LLaMA-Factory 如何支持多轮对话微调,涵盖其基本概念、数据准备、核心机制、评估方法等。简单来说,多轮对话是指包含两个或更多轮次(Turn)的交互过程,其中每一轮通常包含用户的一个输入和模型的一个输出。后续轮次的回应通常依赖于前面轮次的对话内容。模型需要理解并记忆对话历史。

kakaZhui的博客 1309

线程资源PHP源码分析之线程安全模型

文章结束给大家来个程序员笑话:[M]     迎欢载转,载转请注明出处http://blog.youkuaiyun.com/hackooo/article/details/8702156 感谢!新浪微博:小灰马      0.媒介         信相很多人跟我一样,一开始看PHP源码的时候看到一堆的TSRM_CC,TSRM_DC别特蛋疼,大多数函数的声明都会在参数尾末加个TSRM_DC,实着让像我这样...

weixin_34406086的博客 127

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

理解PHP及Zend Engine的线程安全模型(ZTS)

在阅读PHP源码和学习PHP扩展开发的过程中,我接触到大量含有“TSRM”字眼的宏。在查找资料过程中发现有2篇文章写的不错,整理如下: 深入研究PHP及Zend Engine的线程安全模型:http://blog.codinglabs.org/articles/zend-thread-safety.html PHP源码分析之线程安全模型:http://blog.youkuaiyun.com/h

risingsun001的专栏 3709

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

mkdir函数php,从php源码分析mkdir()函数

阅读:1,011本文通过php中mkdir()函数在不同环境下表现结果不一致的现象,分析了php内核对mkdir()函数的实现,引申出php中线程安全与非线程安全两个重要的机制,抛砖引玉,如有表述不妥或者错误之处欢迎指正。0x01 缘起在前阵子分析WORDPRESS IMAGE 远程代码执行漏洞的过程中,在文末提到一点关于php中的mkdir()函数,在触发漏洞时这个地方存在一点疑惑,即当mkd...

weixin_39869593的博客 500

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

深入研究PHP及Zend Engine的线程安全模型

原文地址: 在阅读PHP源码和学习PHP扩展开发的过程中,我接触到大量含有“TSRM”字眼的宏。通过查阅资料,知道这些宏与Zend的线程安全机制有关,而绝大多数资料中都建议按照既定规则使用这些宏就可以,而没有说明这些宏的具体作用。不知道怎么回事总是令人不舒服的,因此我通过阅读源码和查阅有限的资料简要了解一下相关机制,本文是我对研究内容的总结。 本文首先解释了线程安全的概念及

xiaogugood的专栏 888

PHP 的 SAPI 是个什么东西(转)

SAPI,是Server Application Programming Interface的首字母缩写,意思是服务器端应用编程接口。 这是 PHP 内核提供给外部调用其服务的接口,即外部系统可以通过 SAPI 来调用 PHP 提供的编译脚本、执行脚本的服务。 PHP中常用的SAPI有cli、php-fpm,cli是命令行下执行PHP脚本的实现:bin/php script...

anhe0516的博客 323

php mkdir函数_探究PHP中的Mkdir函数

原标题:探究PHP中的Mkdir函数一、起因在复现分析Wordpress-5.0.0 RCE 的时候,因为在写图片的过程中,根据图片的dirname创建目录,而后根据basename写入图片。在目录创建成功的前提下,应该是可以写入文件的。但是情况却不是如此,过程中我要在写目标图片前,必须还要再写一个辅助图片。其实这个辅助图片不是很重要,而重要的是这个辅助图片的目录创建。此过程中例如需要写入目标文件...

weixin_29218963的博客 593

php必备技能!![会补充]

1.解释继承,多态,接口, 2.抽象类和接口的区别 3.php与apache/nginx之间如何通信 深入理解PHP之:Nginx 与 FPM 的工作机制 4.数据库连接池 5.设计模式 6.trait的理解和原理 7.修饰符的解释public/protect/provite 8.进程线程描述 9,.token缓存地址 ...

weixin_38375151的博客 235

蓝奏云解析源码PHP_PHP的灵魂HashTable结构解读

微信公众号:PHP在线说 HashTable 是PHP的灵魂,一点也不为过。在Zend引擎中,比如变量表、常量表、函数表、数组,以及资源管理、线程安全等,其实现都有HashTable的身影。HashTable 是一种查找性能极高的数据结构,理想情况下其算法复杂度是O(1)。PHP 源码信息PHP 版本:php-5.6.17头文件: Zend/zend_hash.h,源文件: Zend/z...

weixin_39793553的博客 363

跟厂长学PHP7内核(三):源码目录结构

上篇文章我们已经介绍了源码分析工具的安装、配置以及调试方法,本文我们来讲述一下PHP源码的目录结构。 一、目录概览 以php-7.0.12为例,看过源码的同学们应该发现源码目录多达十多个,下面是每个目录的说明。 目录 说明 TSRM 线程相关安全的实现 Zend PHP解析器的核心实现 build linux下编译相关的目录 ...

了不起的厂长 569

PHP - 底层/内核 - 学习/实践

本文系统介绍了PHP底层实现原理的学习路径。主要内容包括:1)学习环境搭建(Windows/Mac/Linux);2)核心知识体系(Zend引擎、内存管理、变量实现等);3)扩展开发与性能优化技术;4)调试方法和实践项目建议。文档提供了详细的学习大纲、推荐资源和实践路线图,特别强调对PHP7/8内核改动的深入研究。同时展示了PCNTL扩展的进程管理实例,通过代码演示了多进程创建与等待的实现。学习过程需结合C语言基础、编译原理和计算机体系结构知识,建议从简单扩展开发入手,逐步深入内核机制研究。

"代码"的日常搬运 4476

PHP与Java源码二次开发:从理论到实践

源码二次开发是指在已有源代码基础上进行修改、扩展或优化,以满足特定业务需求或技术目标的开发过程。与从零开发相比,二开具有以下优势:节省开发时间,快速实现业务需求基于成熟框架,稳定性有保障可以借鉴优秀项目的设计思想社区支持丰富,问题容易解决源码二次开发是一项极具价值的技术能力,无论是PHP的快速Web开发还是Java的企业级应用,合理的二开都能显著提升开发效率。关键在于:深入理解原有代码的设计思想遵循最小侵入原则建立完善的测试体系保持与上游版本的同。

2503_92323829的博客 886

探索PHP源码:解析PHP的内部工作原理

PHP 是一种流行的服务器端脚本语言,广泛用于开发动态网页和 Web 应用程序。虽然我们可以使用 PHP 来构建强大的应用程序,但是很少有人深入了解 PHP 的内部工作原理。在本文中,我们将探索 PHP 的源码,深入了解 PHP 是如何工作的。

u011715638的博客 809
上一篇: 简单的多线程流水线模型(二)
下一篇: PHP源码分析之启动过程
小灰马
博客等级 码龄17年 55粉丝 42原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值