Php在其诞生初期,作为一个单进程的CGI程序并没有考虑线程安全,因为没有任何进程能够比一个请求生存期更长。所以一个内部变量能够声明在一个全局范围内,并且可以任意更改,只要它被确切的初始化了。任何没被清理的资源都会随着cgi进程的结束而被释放。
多进程的web server的出现并没有促使TSRM这层的出现,真正促使该层出现的是多线程web server的出现。
非线程的程序中,php仅仅为该进程声明一个变量存储空间就行了。多线程的程序中,则需要为每个线程分配独立的变量存储空间。
php专门有一个线程安全的数据池来保存多线程声明的变量数据
下面是声明线程安全的全局变量
typedef struct {
int sampleint;
char *samplestring;
} php_sample_globals;
int sample_globals_id;
PHP_MINIT_FUNCTION(sample)
{
ts_allocate_id(&sample_globals_id,
sizeof(php_sample_globals),
(ts_allocate_ctor) php_sample_globals_ctor,
(ts_allocate_dtor) php_sample_globals_dtor);
return SUCCESS;
}
我们在程序中这样调用上述struct php_sample_globals的sampleint,SAMPLE_G(sampleint) = 5,这个代码最终是:(((php_sample_globals*)(*((void ***)tsrm_ls))[sample_globals_id-1])->sampleint =
5;
没有线程安全的声明和使用如下:
typedef struct {
int sampleint;
char *samplestring;
} php_sample_globals;
php_sample_globals sample_globals;
PHP_MINIT_FUNCTION(sample)
{
php_sample_globals_ctor(&sample_globals TSRMLS_CC);
return SUCCESS;
}
SAMPLE_G(sampleint) = 5;最终调用的是sample_globals.sampleint = 5
如何开启或者关闭线程安全呢?
在编译时用enable-maintainer-zts选项。在代码中如果要测试是否开启线程安全,使用#ifdef ZTS
当线程安全开启时,一个特殊的指针,叫做tsrm_ls便加到了大多数内部函数的原型里,正是这个指针使得PHP能够区分附着于不同线程的变量的值
#define TSRMLS_D void ***tsrm_ls
#define TSRMLS_DC , void ***tsrm_ls
#define TSRMLS_C tsrm_ls
#define TSRMLS_CC , tsrm_ls
下面第一行是声明,第二行是使用。但是如果没有开启线程安全,则最后一个参数是没有用的
int php_myext_action(int action_id, char *message TSRMLS_DC);
php_myext_action(42, "The meaning of life" TSRMLS_CC);
TSRMLS_FETCH()在使用外部函数库时存取变量的宏,但是需要谨慎的使用这个宏