本文目的
本文探讨了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?