由于这里没有提到(缺乏)锁定需求,所以我查看了 shmop.c 扩展代码。所以,如果我错了,请纠正我,但是 shmop.c 扩展使用 memcpy() 将字符串复制到共享内存中,而无需任何形式的锁定,据我所知,memcpy() 不是原子操作。
如果像我怀疑的那样属实,那么这些“易于使用”的函数就不再那么“易于使用”了,必须用锁(例如信号量、flocks 等)来包装。
由于这里没有提到(缺乏)锁定需求,所以我查看了 shmop.c 扩展代码。所以,如果我错了,请纠正我,但是 shmop.c 扩展使用 memcpy() 将字符串复制到共享内存中,而无需任何形式的锁定,据我所知,memcpy() 不是原子操作。
如果像我怀疑的那样属实,那么这些“易于使用”的函数就不再那么“易于使用”了,必须用锁(例如信号量、flocks 等)来包装。
我在 2003 年写了一个 php memcache,作为一种概念验证。
它用于一些机器上进行繁重的页面负载缓存...
它运行良好。
以下是一些我制作的核心函数
<?php
###############################################
#### 共享内存函数
/*
用于调试这些
使用 `ipcs` 查看当前内存
使用 `ipcrm -m {shmid}` 移除
在某些系统上使用 `ipcclean` 清理未使用的内存,如果您
不想手动操作
*/
###############################################
function get_key($fsize, $file){
if(!file_exists(TMPDIR.TMPPRE.$file)){
touch(TMPDIR.TMPPRE.$file);
}
$shmkey = @shmop_open(ftok(TMPDIR.TMPPRE.$file, 'R'), "c", 0644, $fsize);
if(!$shmkey) {
return false;
}else{
return $shmkey;
}//fi
}
function writemem($fdata, $shmkey){
if(MEMCOMPRESS && function_exists('gzcompress')){
$fdata = @gzcompress($fdata, MEMCOMPRESSLVL);
}
$fsize = strlen($fdata);
$shm_bytes_written = shmop_write($shmkey, $fdata, 0);
updatestats($shm_bytes_written, "add");
if($shm_bytes_written != $fsize) {
return false;
}else{
return $shm_bytes_written;
}//fi
}
function readmem($shmkey, $shm_size){
$my_string = @shmop_read($shmkey, 0, $shm_size);
if(MEMCOMPRESS && function_exists('gzuncompress')){
$my_string = @gzuncompress($my_string);
}
if(!$my_string) {
return false;
}else{
return $my_string;
}//fi
}
function deletemem($shmkey){
$size = @shmop_size($shmkey);
if($size > 0){ updatestats($size, "del"); }
if(!@shmop_delete($shmkey)) {
@shmop_close($shmkey);
return false;
}else{
@shmop_close($shmkey);
return true;
}
}
function closemem($shmkey){
if(!shmop_close($shmkey)) {
return false;
}else{
return true;
}
}
function iskey($size, $key){
if($ret = get_key($size, $key)){
return $ret;
}else{
return false;
}
}
################################################
?>
我已经编写了一个脚本以突出显示共享内存存储的优越性。
虽然它没有使用 shmop 函数,但底层概念是类似的。
'/shm_dir/' 是一个 tmpfs 目录,它基于共享内存,我已将其安装在服务器上。
以下是 Intel Pentium VI 2.8 服务器上的结果
对 1000 个文件的 IO 测试
常规目录的 IO 结果:0.079015016555786
共享内存目录的 IO 结果:0.047761917114258
对 10000 个文件的 IO 测试
常规目录的 IO 结果:3.7090260982513
共享内存目录的 IO 结果:0.46256303787231
对 40000 个文件的 IO 测试
常规目录的 IO 结果:117.35703110695 秒
共享内存目录的 IO 结果:2.6221358776093 秒
100 个文件时差异并不明显也不令人信服。
但是当我们将数量提升到 10000 和 40000 个文件时,很明显共享内存是一个更好的竞争者。
脚本由 http://www.enhost.com 提供
<?php
set_time_limit(0);
// 您的常规目录。确保它具有写入权限
$setting['regular_dir'] = '/home/user/regular_directory/';
// 您的共享内存目录。
$setting['shm_dir'] = '/shm_dir/';
// 要读取和写入的文件数量
$setting['files'] = 40000;
function IO_Test($mode)
{
$starttime = time()+microtime();
global $setting;
for($i = 0 ; $i< $setting['files'] ;$i++)
{
$filename = $setting[$mode].'test'.$i.'.txt';
$content = "Just a random content";
// 只是一些错误检测
if (!$handle = fopen($filename, 'w+'))
{
echo "无法打开文件 ".$filename;
exit;
}
if (fwrite($handle, $content ) === FALSE)
{
echo "无法写入文件 : ".$filename;
exit;
}
fclose($handle);
// 读取测试
file_get_contents($filename);
}
$endtime = time()+microtime();
$totaltime = ($endtime - $starttime);
return $totaltime;
}
echo '<b>对 '.$setting['files']. ' 个文件进行 IO 测试</b><br>';
echo '<b>常规</b> 目录的 IO 结果 : '.IO_Test('regular_dir') .' 秒<br>';
echo '<b>共享内存</b> 目录的 IO 结果 : '.IO_Test('shm_dir') .' 秒<br>';
/* 删除文件以避免低估
#
# 无法删除文件将导致基准测试不准确
# 因为它会导致 IO_Test 函数不会重新创建现有的文件
*/
foreach ( glob($setting['regular_dir']."*.txt") as $filename) {
unlink($filename);$cnt ++;
}
foreach ( glob($setting['shm_dir']."*.txt") as $filename) {
unlink($filename);$cnt ++;
}
?>
Windows 通过内存映射文件支持共享内存。查看以下函数以了解更多详细信息
* CreateFileMapping
* MapViewOfFile
在这个帮助页面中描述的 shmop 实现实际上仅仅是仅存在于 php 中的 ramdisk/tmpfs,甚至只存在于 linux 服务器上。或者我遗漏了什么吗?
在 windows 上,可以通过创建这样的磁盘轻松实现相同的功能。
事实上,在我的服务器上,我使用 tmpfs 磁盘而不是 - 就我看来 - 有限的 shmop 功能。
为什么不实现一个 $_SHARED 或 $_MUTUAL 超级全局变量,我们可以在其中随意创建变量,并且它由所有连接共享?
这将极大地提高许多 PHP 应用程序的性能,并可以节省服务器内存的负担。特别是如果这些变量可以是包含函数的类。
您可以实现它是由程序员来保证原子性。
这样的超级全局变量在 windows 服务器上也是可行的。
SHMOP 背后的理念是易于使用的共享内存接口,
无需在共享内存段中添加任何额外的标头
或需要任何特殊的特殊控件来访问共享内存
段在 PHP 之外。SHMOP 从 C 的 shm api 中借鉴了它的 api,
这使得它非常易于使用,因为它将共享内存像 C 一样视为
一种文件。这使得它即使对于新手来说也非常容易使用,因为
功能。最重要的是 SHMOP 使用 shm 段来存储原始数据,
这意味着当您使用
C、perl 或其他编程语言来打开/创建/读取/写入 shm 段时,您无需担心匹配标头等
由 PHP 创建或将要由 PHP 使用。在这方面,它不同于
sysvshm,其 shm 接口使用一个专门的标头,它驻留在
共享内存段中,这在您
想要从外部程序访问 php shm 时增加了一层不必要的难度。
此外,从我在 Linux 2.2/2.4 和 FreeBSD 3.3 中的个人测试来看,SHMOP 比 sysvshm 快约
20%,主要是因为它不需要解析
专门的标头,并将数据存储为原始形式。
您需要意识到的是,sysvshm 在其能力方面极度面向 php,它与其他非 PHP 实用程序的接口非常混乱。例如,您是否尝试过使用 sysvshm 来读取非 php 创建的 shm 段?这是不可能的,因为 sysvshm 使用一种专有格式,本质上它只能在 PHP 中使用,当然除非您花时间弄清楚这种格式。
因此,基本上,shmop 的目的是提供一个简单的共享内存接口,可以与其他非 php shm 创建器一起使用。
希望这能澄清。