php - 预测在共享内存中存储数据所需的大小

标签 php shared-memory

我正在项目中使用 PHP shm( semaphores extension 的一部分,不要与 shmop 混淆!)函数。基本上共享内存充当一种堆,我只有一个数组,其中我将键(具有无意义的值)存储为散列索引,我只是检查“啊,它已经在那里了”。现在我的问题是:该数组有时会变得很大,但并非总是如此。我不想保留通常不需要的大量内存,而是动态调整大小。

我已经注册了一个错误处理程序,它将错误转换为 ErrorException s,这样我就可以catch shm_put_var 抛出的错误当内存太小而无法存储数组时 - 但不幸的是,当数据放不下时,PHP 会清除该段,因此所有其他数据也会丢失。因此这不是一个选择。

因此,我需要一种方法来预测存储数据所需的大小。 One of the comments to shm_attach at php.net指出 PHP 附加了一个 header (PHP_INT_SIZE * 4) + 8字节长度,一个变量需要 strlen(serialize($foo)) + 4 * PHP_INT_SIZE) + 4 (我已经简化了评论中给出的表达,它与我的相同,但不必要地被放大)
虽然 header 大小似乎是正确的(任何小于 24 字节的内存都会导致创建时出错,因此 24 字节似乎是 PHP 放入其中的 header 的大小),但每个变量条目的大小似乎并不正确。在最新版本的 PHP 中不再适用:
- 我可以将“1”存储在大小为 24 + strlen(serialize("1") + 3 * PHP_INT_SIZE) + 4 的共享内存段中字节(注意其中的 3 而不是 4),
- 我无法以一种尺寸存储“999”24 + strlen(serialize("999") + 4 * PHP_INT_SIZE) + 4

有谁知道一种方法来预测使用 shm 函数在共享内存中存储任何数据需要多少内存,或者对 shm 如何存储变量有一些引用? (我使用 shmop 函数阅读了整个内容并打印了它们,但由于它是二进制数据,因此无法在合理的时间内进行逆向工程)

(我将根据需要提供代码示例,我只是不确定哪些部分会相关 - 如果您想查看任何工作示例,请告诉我,我已经尝试了很多,所以我已经为大多数情况准备了示例)


[更新] 我的 C 非常糟糕,所以我没有深入研究源代码( sysvshm.cphp_sysvshm.h ),但我已经发现了解决方案的一个问题在 php.net 上建议:虽然我可以将那里的复杂公式简化为我在这里包含的内容(基本上取自 C 源代码),但这对于原始公式是不可能的,因为有类型转换并且没有 float 学。该公式除以 sizeof(long)并再次与之相乘 - 这在 PHP 中没有用,但会舍入为 sizeof(long) 的倍数所以我需要先在 PHP 中纠正这个问题。不过,这还不是全部,因为测试表明我可以将一些值存储在比公式返回的内存更少的内存中(见上文)。

最佳答案

作为尝试更新变量时变量被删除并且段中没有足够的可用空间用于新值的问题的解决方法,您可以首先检查是否有足够的可用空间,如果有的话,仅然后您继续更新。
以下函数使用 shmop_* API 获取使用 shm_attach 创建的段中的已用空间、可用空间和总空间。

function getMemSegmentStats($segmentKey){
    $segId = shmop_open($segmentKey, 'a', 0, 0) ;
    $wc = PHP_INT_SIZE/4 ;
    $stats = unpack("I{$wc}used/I{$wc}free/I{$wc}total",shmop_read($segId,8+PHP_INT_SIZE,3*PHP_INT_SIZE)) ;
    shmop_close($segId) ;
    return combineUnpackLHwords($stats) ;
}

function combineUnpackLHwords($array){
    foreach($array as $key => &$val)
        if( preg_match('/([^\d]+)(\d+)/',$key,$matches) ){
            $key2 = $matches[1].($matches[2]+1) ;
            $array[$matches[1]] = $val | $array[$key2] << 4*PHP_INT_SIZE ;
            unset( $array[$key], $array[$key2] ) ;
        }
    return $array ;
}

在 64 位机器中需要函数 combineUnpackLHwords 因为 unpack函数不会解包 64 位整数,因此必须从低位和高位 32 位字构造它们(在 32 位机器上,该函数不起作用)。

示例:

$segmentKey = ftok('/path/to/a/file','A') ;
$segmentStats = getMemSegmentStats($segmentKey) ;
print_r($segmentStats) ;

输出:

Array
(
    [used] => 3296
    [free] => 96704
    [total] => 100000
)

关于php - 预测在共享内存中存储数据所需的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21859118/

相关文章:

php - 按月查询结果

php - 合并两个数组并按日期排序这个新数组

php - 使用 php 将数字添加到获取的数组中

c++ - 以任何方式多次打开共享内存是否不好?

memory-management - C/Pascal 的堆管理器,可自动用零字节填充已释放的内存

使用正在运行的进程的共享内存收集核心转储

PHP PDO 连接失败但没有错误

php - 报告垃圾邮件或滥用行为

c - sem_open 不适用于 Ubuntu : undefined reference to `sem_open'

c - 共享内存错误 : shmat return NULL , errno(22 :Invalid argument) in CentOS6. 8