shm_put_var 没有防止竞争条件的保护。如果两个脚本同时插入相同的键,php 可能会出现段错误。
(PHP 4, PHP 5, PHP 7, PHP 8)
shm_put_var — 在共享内存中插入或更新变量
shm_put_var() 使用给定的 key
插入或更新 value
。
如果 shm
不是有效的 SysV 共享内存索引,或者剩余的共享内存不足以完成请求,则会发出警告(E_WARNING
等级)。
shm
从 shm_attach() 获取的共享内存段。
key
变量键。
value
变量。所有变量类型 serialize() 支持,通常这意味着除资源和某些无法序列化的内部对象之外的所有类型。
很遗憾,troy是对的。
以下脚本将返回
resource(5) of type (stream)
int(0)
<?php
define("FOPEN_RESOURCE", 1);
define("FOPEN_FILEPATH", "/path/to/file");
$fopen_resource = fopen(FOPEN_FILEPATH, "w");
var_dump($fopen_resource);
$shm_id = shm_attach(1);
if ($shm_id === false)
{
echo "Fail to attach shared memory.\n";
}
if (!shm_put_var($shm_id, FOPEN_RESOURCE, $fopen_resource))
{
echo "Failed to put var 1 in shared memory $shm_id.\n";
}
$sm_fopen_resource = shm_get_var($shm_id, FOPEN_RESOURCE);
if ($sm_fopen_resource === false)
{
echo "Failed to retreive fopen_resource from Shared memory\r\n";
}
var_dump($sm_fopen_resource);
if($shm_id) shm_remove($shm_id);
if($fopen_resource) fclose($fopen_resource);
?>
它会支持像 pfsockopen() 指针这样的资源标识符吗?
主要问题是,当我们将 PHP 作为 Apache 模块运行时,我们永远不知道下一个请求将绑定到哪个进程,这使得不可能拥有真正的持久套接字连接,除非我们可以将指向它的指针存储起来,或者直接使用 fopen() 之类的函数打开套接字 inode 并再次检索相同的资源指针。
我以为我可以使用 shm,但似乎 shm 不允许存储资源指针……太可惜了……:(
引用:“它会支持像 pfsockopen() 指针这样的资源标识符吗……我们使用 Apache 模块运行 PHP……没有办法拥有真正的持久套接字”
对不起,但这对我来说没有意义……如果您想恢复套接字,只需使用相同的 host 和 port 调用 pfsockopen() - 您将获得相同的套接字。无需传递实际的资源变量。
如果每个套接字都有一些惊人地特殊和独特的特性,您可以执行以下操作 - 这应该适用于任何持久资源
为了区分或获取特定的资源 - 只需序列化/存储每个资源唯一 ID 的索引,以及使该资源唯一的细节。
您可以像这样获得一个唯一的资源标识符作为整数值
<?php
$rid = str_replace("Resource id #", "", print_r($fp, true));
// $rid = 2
?>
由于 pfsockopen() 使用主机名和端口作为恢复持久连接的唯一键,您可以添加 DNS 通配符或在 /etc/hosts(或 windows 等效项)中添加多个手动条目,如下所示
resource-0.host.com 192.168.100.1
resource-1.host.com 192.168.100.1
resource-2.host.com 192.168.100.1
resource-3.host.com 192.168.100.1
然后,在查阅您序列化后的资源列表后,您可以使用其资源 ID 连接到特定资源。
例如:`$pf = pfsockopen("resource-$rid.host.com", $port, $timeout);`
新资源在各个方面都将与原始资源相同。
对于基于文件的流资源,您可以使用符号链接执行类似的操作,或者使用下一种方法……
对于基于 URL 的资源或其他具有“路径”的资源(我不知道是否存在涉及此类事物的持久性函数),您可以使用路径中的额外信息来区分它们。例如
http://host.com/resource-4/../script.php
http://[email protected]/script.php
/tmp/././././file.txt
在第一个示例中,多余的“resource-4”将被 web 服务器忽略。
在第二个示例中,多余的用户名将被 web 服务器忽略。(对于 `mysql_pconnect`,可以使用多个用户名执行类似的操作)。
在第三个示例中,四个连续出现的“无效”字符串“./”将表示资源#4。
如果这还不够,您可以使用 PHP 共享内存资源本身与它们的 .c 对应部分创建的资源互操作的事实。这允许您编写一个精简的 .c 应用程序来处理繁重的工作。
或者,您可以尝试使用持久性流和上述方法重新连接到您自己的 web 服务器,以达到最终结果。我想不出任何需要如此极端方法的例子,但我相信这并非不可能。
我个人使用一个 117 MB 的二进制数据库,该数据库存储在共享内存中,既可从命令行(使用编译的 .c 应用程序)访问,也可从 web(通过 PHP 和 `ftok()`/`shmop_open()`/`shmop_read()`)访问。
是的,可以使用共享内存保持真正的资源持久性级别。PHP 中的所有变量都作为 zval 存储在公共哈希表中,包括资源标识符。只要整个 PHP 进程没有关闭,就有可用的哈希表可以超出请求的生存期。您只需要将标识符存储在此类哈希表中并找到跟踪它们的方法,您就可以接收原始资源。
我不知道为什么 PHP 没有提供设置/获取持久性资源的方法,但这可能是由于它提供了许多类型的 SAPI,并且并非所有 SAPI 都能实现此功能,包括仍然远未过时的 CGI。
另一个问题是,在用户空间中拥有这样的访问权限必然会导致多个 PHP 进程尝试访问同一资源的问题。从这个角度来看,如果没有更好的同步机制,实际上没有安全的方法来安全地使用这样的 getter/setter。
尽可能少用变量键。对于大型数据数组,最好使数组多维并在一个变量键下存储,而不是使用变量键作为索引。当需要反复从数组末尾获取数据并且更新频率较低时,这种好处尤其明显。