本文目的
本文探讨了php标准扩展sysvshm库中的函数shm_get_var返回变量的copy还是reference。
问题背景
php标准扩展中的sysvshm提供了php访问共享内存的能力,而且数据的最小粒度是变量,这样可以免去手动变量序列化和反序列化,比另一个标准扩展shmop好使用。但是,如果假设有一个50M的数组放到共享内存中,反复的访问此变量,是否会影响系统性能?如果shm_get_var返回的是变量引用,那么性能开销不大,因为每次返回的都只是内存的“指针”,不会拷贝真实数据。如果返回的是copy呢?那么每访问一次,就需要拷贝50M左右的内存,如果访问频率过大,那么就会导致性能瓶颈。
验证
到底返回什么,通过下面的代码,可以得到验证:
<?php
$arr = array(0,1,2,3,4,5,6);
if(!($nShmID = shm_attach(ftok(__FILE__, 'i'), 1024))) {
die("create shared memory failed.\n");
}
$nVarKey = 1;
if(!shm_put_var($nShmID, $nVarKey, $arr)) {
die("failed to put var\n");
}
if(!($arr1 = shm_get_var($nShmID, $nVarKey))) {
die("failed to get arr1\n");
}
array_pop($arr1);
if(!($arr2 = shm_get_var($nShmID, $nVarKey))) {
die("failed to get arr2\n");
}
if ($arr != $arr2) {
echo "get a copy\n";
} else {
echo "get a reference\n";
}
if(!shm_remove($nShmID)) {
die("failed to remove shared memory\n");
}
?>
执行结果如下:
所以,很明显,返回的是copy而不是reference。
原始代码
为什么会返回copy呢?可以看看shm_get_var的c代码实,文件位置PHP_SRC/ext/sysvshm/sysvshm.c,如下:
/* {{{ proto mixed shm_get_var(resource id, int variable_key)
Returns a variable from shared memory */
PHP_FUNCTION(shm_get_var)
{
zval *shm_id;
long shm_key;
sysvshm_shm *shm_list_ptr;
char *shm_data;
long shm_varpos;
sysvshm_chunk *shm_var;
php_unserialize_data_t var_hash;
if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &shm_id, &shm_key)) {
return;
}
SHM_FETCH_RESOURCE(shm_list_ptr, shm_id);
/* setup string-variable and serialize */
/* get serialized variable from shared memory */
shm_varpos = php_check_shm_data((shm_list_ptr->ptr), shm_key);
if (shm_varpos < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "variable key %ld doesn't exist", shm_key);
RETURN_FALSE;
}
shm_var = (sysvshm_chunk*) ((char *)shm_list_ptr->ptr + shm_varpos);
shm_data = &shm_var->mem;
PHP_VAR_UNSERIALIZE_INIT(var_hash);
if (php_var_unserialize(&return_value, (const unsigned char **) &shm_data, (unsigned char *) shm_data + shm_var->length, &var_hash TSRMLS_CC) != 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "variable data in shared memory is corrupted");
RETVAL_FALSE;
}
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
}
/* }}} */
从上面的代码,可以知道shm_get_var的内部实现会调用php_var_unserialize进行反序列化,也就表示变量其实还是以序列化后的字节流形式存放在共享内存中,这样必然无法返回变量引用,只能返回拷贝。shm_get_var只是为我们做了反序列化工作,本质上与shmop_read一样。
结论
由于php_get_var返回的是变量拷贝,如果需要反复访问共享内存中的较大的变量时,最好换一种存储策略,因为共享内存会重复拷贝数据,造成不必要的性能开销。
参考资料
stackOverflow: Does PHP copy variables when retrieving from shared memory?