2024年PHP开发者大会(日本)

共享内存函数

目录

添加备注

用户贡献的备注 9 条备注

Craig Manley
19年前
由于这里没有提到(缺乏)锁定需求,所以我查看了 shmop.c 扩展代码。所以如果我错了请纠正我,但是 shmop.c 扩展使用 memcpy() 将字符串复制到共享内存和从共享内存复制出来,没有任何形式的锁定,据我所知,memcpy() 不是原子的。

如果我的猜测正确,那么这些“易于使用”的函数就不再那么“易于使用”了,必须用锁(例如信号量、flock 等)来包装。
joeldg at gmail.com
18年前
我在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;
}
}
################################################
?>
Roy <roy AT enhost.com>
18年前
我编写了一个脚本来突出共享内存存储的优越性。
虽然它没有使用 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 ++;
}

?>
hackie at prohost dot org
19年前
shmop 扩展的职责并非提供锁定机制,有很多可用的锁定方案,如果您需要某种原子操作,请选择适合您的锁定方案并使用它。
stoimenov at email dot com
22 年前
Windows 通过内存映射文件支持共享内存。查看以下函数以了解详细信息

* CreateFileMapping
* MapViewOfFile
Arikania
6年前
此帮助页面中描述的 shmop 实现实际上只是一个仅存在于 PHP 中,甚至仅存在于 Linux 服务器上的 ramdisk/tmpfs。或者我错过了什么?

在 Windows 上,可以通过创建这样的磁盘轻松实现相同的功能。

事实上,在我的服务器上,我使用的是 tmpfs 磁盘,而不是——在我看来——有限的 shmop 功能。

为什么不实现一个 $_SHARED 或 $_MUTUAL 超全局变量,我们可以随意创建变量,并且所有连接都可以共享?

这将极大地提高许多 PHP 应用程序的性能,并可以节省大量服务器内存开销。特别是如果这些变量可以包含函数的类。

您可以实现由程序员来保证原子性。

这种超全局变量在 Windows 服务器上也是可行的。
rei at prohost dot org
23年前
SHMOP 背后的理念是一个易于使用的共享内存接口,
无需向共享内存段添加任何额外的标头
也不需要任何特殊的控制来访问 PHP 之外的共享内存
段。SHMOP 从 C 的 shm api 借用其 api,
这使得它非常易于使用,因为它像 C 一样将共享内存视为
一种文件。由于此功能,即使是新手也很容易使用
此功能。最重要的是 SHMOP 使用 shm 段来存储原始数据,
这意味着当您
使用 C、perl 或其他编程语言打开/创建/读取/写入由 PHP 创建或将由 PHP 使用的 shm 段时,您无需担心匹配标头等。
在这方面,它与
sysvshm 不同,sysvshm 的 shm 接口使用一个特殊的标头,该标头位于
共享内存段内,当您
想要从外部程序访问 php shm 时,这会增加不必要的难度。
此外,根据我在 Linux 2.2/2.4 和 FreeBSD 3.3 中的个人测试,SHMOP 比 sysvshm 快约
20%,主要是因为它不需要解析
专用标头并将数据存储为原始格式。
eddeeboo at gmail dot com
4天前
尽管共享内存的读取和写入不是原子的,但读取和写入单个字节始终是原子的。如果您的应用程序经常读取而很少写入“小”数据块(约 10-15 字节),这将非常有用。您可以通过使用 8 位校验和(如 CRC-8)对数据进行签名来避免使用任何类型的锁。这是确保数据不会损坏的有效且可靠的方法。冗余自然为 8 位。
slavapl at mailandnews dot com
23年前
您需要意识到的是,sysvshm 在其能力上极度面向 php,它与其他非 PHP 实用程序的接口相当笨拙。例如,您是否尝试过使用 sysvshm 读取非 php 创建的 shm 段?这是不可能的,因为 sysvshm 使用专有格式,实质上它只能在 PHP 内使用,除非您花时间弄清楚这种格式。
因此,基本上,shmop 的目的是提供一个简单的共享内存接口,可以与其他非 php shm 创建者一起使用。

希望这能澄清。
To Top