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() 支持:通常这意味着除了资源和一些无法序列化的内部对象之外的所有类型。
可悲的是,特洛伊是对的
以下脚本将返回
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() 指针这样的资源标识符吗... 我们将 PHP 作为 Apache 模块运行... 无法拥有真正的持久套接字”
抱歉,但我无法理解... 套接字仍然是持久的,如果您想恢复它,只需使用相同的主机和端口调用 pfsockopen() - 您将获得相同的套接字。无需传递实际的资源变量。
如果每个套接字都有一些惊人特殊和独特的方面,您可以执行以下操作 - 这应该适用于任何持久资源
为了区分或获取特定资源 - 只需序列化/存储每个资源唯一 ID 的索引,以及使该资源唯一的详细信息。
您可以像这样获取唯一的资源标识符作为整数的值
<?php
$rid = str_replace("Resource id #", "", print_r($fp, true));
// $rid = 2
?>
由于 pfsockopen() 使用主机名和端口作为唯一键来恢复持久连接,您可以在 /etc/hosts 中添加 DNS 通配符或多个手动条目(或 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 中的所有变量都存储在公共哈希表中,作为 zvals,包括资源标识符。有一些哈希表可以比请求持续更长时间,只要整个 PHP 进程没有关闭。您只需要将标识符存储在这样的哈希表中,以及跟踪它们的方法,您就可以接收原始资源。
我不知道为什么 PHP 没有提供设置/获取持久资源的方法,但这可能是因为 PHP 有多种 SAPI 可用,而且并非所有 SAPI 都*可以*支持此功能,包括 CGI,它仍然远未过时。
另一个问题是,在用户空间中拥有这样的访问权限可能会导致多个 PHP 进程尝试访问同一个资源的问题。从这个角度来看,如果没有更好的同步机制,在 PHP 中使用这样的 getter/setter 实际上没有安全的方法。
尽可能少用 variable_keys。对于大型数据数组,最好将数组设为多维数组并在一个 variable_key 下存储,而不是使用 variable_key 作为索引。这种方法在需要从数组末尾重复获取数据并且更新不太频繁的情况下尤其有效。