shm_attach

(PHP 4, PHP 5, PHP 7, PHP 8)

shm_attach创建或打开共享内存段

说明

shm_attach(int $key, ?int $size = null, int $permissions = 0666): SysvSharedMemory|false

shm_attach() 返回一个可用于访问给定 key 的 System V 共享内存的 ID,第一次调用会使用 size 和可选的权限位 permissions 创建共享内存段。

对同一个 key 的第二次 shm_attach() 调用将返回一个不同的 SysvSharedMemory 实例,但这两个实例都访问同一个底层共享内存。 sizepermissions 将被忽略。

参数

key

一个数值共享内存段 ID

size

内存大小。如果未提供,则默认为 php.ini 中的 sysvshm.init_mem,否则为 10000 字节。

permissions

可选的权限位。默认为 0666。

返回值

成功时返回 SysvSharedMemory 实例,失败时返回 false

变更日志

版本 说明
8.0.0 成功时,此函数现在返回 SysvSharedMemory 实例;以前返回的是 resource
8.0.0 size 现在可以为空。

参见

  • shm_detach() - 从共享内存段断开连接
  • ftok() - 将路径名和项目标识符转换为 System V IPC 密钥

添加注释

用户贡献的注释 24 个注释

5
Uther Pendragon
17 年前
由于没有为创建和附加共享内存提供单独的函数(我认为这是一个错误),您可能需要进行一些测试来检查您是否已创建它,因为您可能不希望主从对的从机永远创建共享内存。

您可以测试它的一个方法是让您的从机将大小设置为一个较小的值,然后通过放置一个大于大小的变量来测试该大小,例如:

function get_shmem() {
return shm_attach(ftok('somefile.txt', 'T'), 100, 0644);
}

$shm = get_shmem();

while (!@shm_put_var($shm, 1, str_repeat('.....', 20))) {
shm_remove($shm);
sleep(1);
//我们创建了它,所以我们将删除它并休眠以等待主服务器创建它,然后重试。
$shm = get_shmem();
}
shm_remove_var($shm, 1);
//在这里我们知道共享内存已经被创建了,因为我们可以在请求的大小中放入一个更大的变量。

另一种处理方法是让所有程序使用相同的参数初始化共享内存... 我在客户端启动速度过快并创建了 shmem 却没有传递 memsize 值时遇到了这个问题,所以它默认值为 10k,这太小了。
8
novayear # hotmail ; com
10 年前
小型 shm 类..
示例用法
$shx= new shmSmart;
$shx->put("key_name_apple","key_val_peach"); //设置示例..
$shx->put("key name alternative array",array(1=>"banana","apricot","blablabla"=>array("new-blaala"))); //设置数组示例..
echo $shx->get("key_name_apple"); // 获取示例键值。
$shx->del("key_name_apple"); // 删除键
unset($shx); // 释放 PHP 中的内存..

class shmSmart{
public $shm; //保存共享内存资源
public function __construct(){
if(function_exists("shm_attach")===FALSE){
die("\n您的 PHP 配置需要调整。请参阅:http://us2.php.net/manual/en/shmop.setup.php. 要启用 System V 共享内存支持,请使用选项 --enable-sysvshm 编译 PHP。");
}
$this->attach(); //创建资源(共享内存)
}
public function attach(){
$this->shm=shm_attach(0x701da13b,33554432); //分配共享内存
}
public function dettach(){
return shm_detach($this->shm); //分配共享内存
}
public function remove(){
return shm_remove($this->shm); //取消分配共享内存
}
public function put($key,$var) {
return shm_put_var($this->shm,$this->shm_key($key),$var); //存储变量
}
public function get($key){
if($this->has($key)){
return shm_get_var($this->shm,$this->shm_key($key)); //获取变量
}else{
return false;
}
}
public function del($key){
if($this->has($key)){
return shm_remove_var($this->shm,$this->shm_key($key)); // 删除变量
}else{
return false;
}
}
public function has($key){
if(shm_has_var($this->shm,$this->shm_key($key))){ // 检查是否已设置
return true;
}else{
return false;
}
}
public function shm_key($val){ // 支持所有世界语言和字符!
return preg_replace("/[^0-9]/","",(preg_replace("/[^0-9]/","",md5($val))/35676248)/619876); // 文本到数字系统。
}
public function __wakeup() {
$this->attach();
}
public function __destruct() {
$this->dettach();
unset($this);
}
}
3
Uther Pendragon
17 年前
作为我对上一篇关于 shm_attach 及其限制能力的帖子的后续...

为了获得更多控制,请使用 shmop_* 系列函数,因为它们比这些函数具有更细粒度的控制。

顺便说一下:SHMOP 函数应该在所有 SHM* 包装函数的“另请参见”中列出(我假设它们是 SHMOP* 函数的包装函数)。
3
h dot raaf at i-k-c dot net
25 年前
请注意,共享内存的“int key”与用于信号量的键共享。因此,当您对信号量和共享内存使用相同的键值时,您会遇到麻烦!
2
nathanbruer at gmail dot com
13 年前
我在玩这些函数,并在这个过程中创建了一个类。当然,这将比本地访问变量慢,但它提供了在共享环境中存储变量的能力,并让许多运行的脚本了解应该从共享区域访问它们。这还应该在没有更多脚本链接到数据时自动销毁共享内存区域(当所有脚本使用此类时)。

<?php
class SharedMemory{
private
$nameToKey = array();
private
$key;
private
$id;
function
__construct($key = null){
if(
$key === null){
$tmp = tempnam('/tmp', 'PHP');
$this->key = ftok($tmp, 'a');
$this->id = shm_attach($this->key);
$this->nameToKey[] = '';
$this->nameToKey[] = '';
$this->updateMemoryVarList();
shm_put_var($this->id, 1, 1);
}else{
$this->key = $key;
$this->id = sem_get($this->key);
$this->refreshMemoryVarList();
shm_put_var($this->id, 1, shm_get_var($this->id, 1) + 1);
}
if(!
$this->id)
die(
'Unable to create shared memory segment');
}
function
__sleep(){
shm_detach($this->id);
}
function
__destruct(){
if(
shm_get_var($this->id, 1) == 1){
// I am the last listener so kill shared memory space
$this->remove();
}else{
shm_detach($this->id);
shm_put_var($this->id, 1, shm_get_var($this->id, 1) - 1);
}
}
function
__wakeup(){
$this->id = sem_get($this->key);
shm_attach($this->id);
$this->refreshMemoryVarList();
shm_put_var($this->id, 1, shm_get_var($this->id, 1) + 1);
}
function
getKey(){
return
$this->key;
}
function
remove(){
shm_remove($this->id);
}
function
refreshMemoryVarList(){
$this->nameToKey = shm_get_var($this->id, 0);
}
function
updateMemoryVarList(){
shm_put_var($this->id, 0, $this->nameToKey);
}
function
__get($var){
if(!
in_array($var, $this->nameToKey)){
$this->refreshMemoryVarList();
}
return
shm_get_var($this->id, array_search($var, $this->nameToKey));
}
function
__set($var, $val){
if(!
in_array($var, $this->nameToKey)){
$this->refreshMemoryVarList();
$this->nameToKey[] = $var;
$this->updateMemoryVarList();
}
shm_put_var($this->id, array_search($var, $this->nameToKey), $val);
}
}

// Example
$sharedMem = new SharedMemory();
$pid = pcntl_fork();
if(
$pid){
//parent
sleep(1);
echo
"Parent Says: " . $sharedMem->a . "\n";
echo
"Parent Changed to 0\n";
$sharedMem->a = 0;
//Parent just changed it to 0
echo "Parent Says: " . $sharedMem->a . "\n";
sleep(2);
// Parent think's it's 0, but child has changed it to 1
echo "Parent Says: " . $sharedMem->a . "\n";
}else{
//child
$sharedMem->a = 2;
echo
"Child Changed to 2\n";
// Should be 2 as child just set it to 2
echo "Child Says: " . $sharedMem->a . "\n";
sleep(2);
// Child think's it's 2, but the parent set it to 0.
echo "Child Says: " . $sharedMem->a . "\n";
echo
"Child Added 1\n";
$sharedMem->a++;
echo
"Child Says: " . $sharedMem->a . "\n";
}
?>
2
Daniel Knecht
10 年前
如果您遇到类似“PHP Warning: shm_attach(): failed for key 0x61040bb5: Cannot allocate memory”的错误,则可能需要调整您的共享内存配置。

要查看系统值,请输入“sysctl kern.sysv”。
重要值是 kern.sysv.shmmax 和 kern.sysv.shmall
* kern.sysv.shmmax 是一个共享内存段可以拥有的最大字节数
* kern.sysv.shmall 是所有共享内存段可以消耗的最大内存页面数
一个内存页为 4096 字节,这意味着如果您将 kern.sysv.shmmax 设置为 1073741824 (1GB),则 kern.sysv.shmall 必须至少为 262144 才能分配一个 1GB 内存段(因为 262144 * 4096 = 1073741824)。

tl;dr 一些系统上的默认值非常低,因此仅仅增加 kern.sysv.shmmax 还不够 - kern.sysv.shmall 也需要相应地增加!
2
webmaster at mail dot communityconnect dot com
24 年前
使用 Sun Solaris 2.x 时,允许的最大共享内存值为 1,048,576。可以使用命令 /usr/sbin/sysdef 确定允许的最大值。在 Linux 上,似乎没有系统强制的最大值。要更改 Solaris 2.x 上允许的最大值,请使用 set shmsys:shminfo_shmmax=[新值]。
1
zeppelinux at comcast dot net
16 年前
<?php

// 如何计算存储变量 $foo 所需的最小 $memsize,其中 $foo='foobar'。

// 当第一次调用 shm_attach() 时,PHP 会将一个头部写入共享内存的开头。
$shmHeaderSize = (PHP_INT_SIZE * 4) + 8;

// 当调用 shm_put_var() 时,变量会被序列化,并且在它被写入共享内存之前,会在它的前面放置一个小的头部。
$shmVarSize = (((strlen(serialize($foo))+ (4 * PHP_INT_SIZE)) /4 ) * 4 ) + 4;

// 现在将两者加起来以获得所需的总内存。 当然,如果您存储多个变量,则不需要为每个变量添加 $shmHeaderSize,只需要添加一次。
$memsize = $shmHeaderSize + $shmVarSize;

// 这将为您提供使用 shm_put_var() 存储一个变量所需的足够内存。
$shm_id = shm_attach ( $key, $memsize, 0666 ) ;
shm_put_var ( $shm_id , $variable_key , $foo );

任何尝试存储另一个变量的操作都将导致 '内存不足' 错误.
请注意,如果 您将 $foo 的内容更改为更大的值,然后 尝试 使用 shm_put_var() 再次将其写入共享内存,那么 您将收到 '内存不足' 错误. 在这种情况下, 您将必须调整共享内存段的大小,然后 写入 新的 .

如果
您只存储包含单个整数值的变量,那么 您可以避免通过始终分配存储 int 所需的最大内存量来调整大小,这应该是
$shmIntVarSize = (((strlen(serialize(PHP_INT_MAX))+ (4 * PHP_INT_SIZE)) /4 ) * 4 ) + 4;

?>
1
hetii at poczta dot onet dot pl
16 年前
你好 :)
我写了一个小类来构建我的应用程序之间的明文消息。

<?
类 Bright_Message
{

var $bright;
var $SHM_KEY;
var $my_pid;

函数 Bright_Message($SHM_KEY=null)
{
$this->my_pid = getmypid();//获取自己的 pid
如果 (is_null($SHM_KEY)) $this->SHM_KEY = '123123123';
$this->bright = shm_attach($this->SHM_KEY, 1024, 0666);
$this->one_instance();
}

函数 get_msg($id,$remove=true)
{
如果(@$msg=shm_get_var($this->bright,$id))
{
如果 ($remove) @shm_remove_var($this->bright,$id);
返回 $msg;
} 否则 返回 false;
}

函数 snd_msg($id,$msg)
{
@shm_put_var($this->bright,$id,$msg);
return true;
}

函数 one_instance()
{
$SHM_PID = $this->get_msg(1,false);
如果((strpos(exec('ps p'.$SHM_PID),$_SERVER['SCRIPT_FILENAME'])) === false)
$this->snd_msg(1,$this->my_pid); 否则
{
回显 "这个程序存在于 pid: $SHM_PID\r\n\r\n";
退出;
}
}

}
?>

send.php
<?
包含 "bridge_message.class.php";
$shm = new Bright_Message();
$shm->snd_msg(2,'这是一条简单的消息');
?>

receive.php
<?
包含 "bridge_message.class.php";
$shm = new Bright_Message();
$msg = get_msg(2);
回显 print_r($msg,1);
?>
1
jpeter1978 at yahoo dot com
17 年前
我尝试了上面所有关于获取 $memsize 的对象大小(以字节为单位)的建议,但它们对尝试的两种对象类型(字符串和字符串数组)都不起作用。

在进行了一些谷歌搜索和实验之后,我发现了以下神奇公式

$memsize = ( strlen( serialize( $object ) ) + 44 ) * 2;

我在其他人的代码中发现了这一点,所以我无法解释它。
1
Katzenmeier
17 年前
一个 SHM 块的限制似乎是 32 MB,但如果您需要,可以将您的数据拆分为多个 SHM 块。 总的 SHM 限制似乎大约为 8 GB。

我不确定这对所有配置是否属实。
1
muytoloco at yahoo dot com dot br
18 年前
如果一个进程对一个不存在的内存区域执行 shm_attach,则该区域将在运行脚本用户的相同权限下创建。 如果另一个进程试图创建或访问相同的区域,该进程由另一个用户运行,并且权限与第一个用户不同,则会发生错误。
1
rch at todo dot com dot uy
19 年前
Cecil,变量的键是整数(不是名称)。 你可以在同一个共享内存中放置多个变量。

#!/usr/local/bin/php -q
<?PHP

$SHM_KEY
= ftok(__FILE__, chr( 4 ) );

$data = shm_attach($SHM_KEY, 1024, 0666);

$test1 = array("hello","world","1","2","3");
$test2 = array("hello","world","4","5","6");
$test3 = array("hello","world","7","8","9");

shm_put_var($data, 1, $test1);
shm_put_var($data, 2,$test2);
shm_put_var($data, 3,$test3);

print_r(shm_get_var($data, 1));
print_r(shm_get_var($data, 2));
print_r(shm_get_var($data, 3));

shm_detach($data);
?>
1
andreyKEINSPAM at php dot net
20 年前
据我从 ext/sysvshm 源代码中看到的情况,不需要对 "perm" 进行按位或 (|) 操作,其中 "perm" 与 IPC_CREAT(和 IPC_EXCL)相或。 扩展程序会为你处理。 它尝试附加到内存段,如果尝试失败,它会尝试附加,但标志设置为 user_flag | IPC_CREAT | IPC_EXCL。
确切的代码 (shm_flag 是函数的第三个参数)
如果 ((shm_id = shmget(shm_key, 0, 0)) < 0) {
如果 (shm_size < sizeof(sysvshm_chunk_head)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%x: memorysize too small", shm_key);
efree(shm_list_ptr);
RETURN_FALSE;
}
如果 ((shm_id = shmget(shm_key, shm_size, shm_flag | IPC_CREAT | IPC_EXCL)) < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%x: %s", shm_key, strerror(errno));
efree(shm_list_ptr);
RETURN_FALSE;
}
}
1
Cecil
20 年前
以下是如何使用一个共享内存块存储多个变量或数组的示例。 不幸的是,为了存储多个变量,您必须多次使用 sem_get()。 shm_attach()、shm_put_var() 和 shm_get_var() 也是如此。

#!/usr/local/bin/php -q
<?PHP
// test.php

$SHM_KEY = ftok(__FILE__,'A');

$shmid = sem_get($SHM_KEY, 1024, 0644 | IPC_CREAT);
$shmid2 = sem_get($SHM_KEY, 1024, 0644 | IPC_CREAT);
$shmid3 = sem_get($SHM_KEY, 1024, 0644 | IPC_CREAT);

$data = shm_attach($shmid, 1024);
$data2 = shm_attach($shmid2, 1024);
$data3 = shm_attach($shmid3, 1024);

$test = array("hello","world","1","2","3");
$test2 = array("hello","world","4","5","6");
$test3 = array("hello","world","7","8","9");

shm_put_var($data,$inmem,$test);
shm_put_var($data2,$inmem2,$test2);
shm_put_var($data3,$inmem3,$test3);

print_r(shm_get_var($data,$inmem));
print_r(shm_get_var($data2,$inmem2));
print_r(shm_get_var($data3,$inmem3));

shm_detach($data);
shm_detach($data2);
shm_detach($data2);
?>

为了真正地测试它,创建一个类似的第二个脚本并运行它。

#!/usr/local/bin/php -q
<?PHP
// test2.php

$SHM_KEY = ftok(__FILE__,'A');

$shmid = sem_get($SHM_KEY, 1024, 0644 | IPC_CREAT);
$shmid2 = sem_get($SHM_KEY, 1024, 0644 | IPC_CREAT);
$shmid3 = sem_get($SHM_KEY, 1024, 0644 | IPC_CREAT);

$data = shm_attach($shmid, 1024);
$data2 = shm_attach($shmid2, 1024);
$data3 = shm_attach($shmid3, 1024);

print_r(shm_get_var($data,$inmem));
print_r(shm_get_var($data2,$inmem2));
print_r(shm_get_var($data3,$inmem3));

shm_detach($data);
shm_detach($data2);
shm_detach($data2);
?>

正如你所见,test2.php 没有向共享内存中插入任何内容,但它却能提取出已经存储好的三个完全不同的数组。

希望这能有所帮助。我花了点时间才弄清楚。每个人似乎都有自己对 shm 使用方式的想法。哈哈。

顺便说一句,我不确定 ftok 是如何工作的,因为我没有更改 __FILE__ 以匹配 test.php 的文件路径或任何其他内容。我认为文件路径必须完全相同才能正常工作。好吧,它按原样工作了!哈哈。

- Cecil
1
eric at superstats dot com
25 年前
对象在 shm_put_var 中以序列化方式存储,因此要查找 memsize 的值,可以使用 strlen(serialize($object_to_store_in_shm))。
0
bobhairgrove
6 年前
在“Notes”部分中,它指出,从 PHP 5.3.0 开始,可以将 shm_attach() 返回的资源 ID 转换为整数;但是,这与 Linux 上从原生 SysV 函数 shmget() 返回的 shmid 值并不相同(正如我所希望的那样)。通过强制转换返回的整数仅仅是资源的 ID 号,在这些函数的上下文中之外完全没有用处,AFAICT。

为了验证这一点,可以使用 shm_attach()(或 shmop_open())分配内存块,打印出返回的资源,并将其与在 Linux(或其他 *nix 操作系统)上运行“ipcs”时“shmid”列中的值进行比较。
0
somepay at gmail dot com
14 年前
在我的日志中,我发现字符串
"shm_attach(): failed for key 0x366f: No space left on device"
但在 php.net 和 google 上都没有找到对此的任何建议。

因此,问题是如何释放 shm_attach 使用的内存。
首先查看(Linux)分配了多少段,使用
:~# ipcs -mu
然后
限制
:~# ipcs -ml

要移除段,使用
:~# ipcrm -m [shmid]

否则,你可以重启服务器,或运行基于上述命令的 sh 脚本。

为了避免 "No space left on device" 的问题,在调用 shm_attach() 后,始终使用
shm_remove() & shm_detach()。
0
koester at x-itec dot de
23 年前
在 FreeBSD 上,4194304 表示 4 MB,而不是 4 GB 共享内存。如果你愿意,可以在运行时增加共享内存。
-1
info at tristat dot org
12 年前
你可能期望 SHM 函数比从 MySQL 操作构建变量要快得多。不幸的是,对于大型多维数组来说,情况并非如此。我已经测试了使用 SHM 直接写入、读取、更新和删除二维关联数组,并与使用 MySQL 的类和使用 SHMOP 函数的类进行了比较(使用主要偏移量准确的序列化)。这两个类都生成了与使用 SHM 存储的数组相同的数组。与 SHMOP 不同的是,SHM 的性能随着数组变大而大幅下降。当 $array[$row][$key] 为 200 行 5 个键时,即使是 MySQL 也比 SHM 快,即使执行了 2000 次指令。这可能是因为 SHM 处理任何类型的数组,并在内部使用了 PHP 的强大序列化() 函数。
-1
pail dot luo at gmail dot com
15 年前
一个简单的示例,介绍 SHM。

<?php
if ( sizeof($argv)<2 ) {
echo
"使用方法: $argv[0] send|recv|rem|dele ID [消息] \n\n" ;
echo
" 例如: $argv[0] send 1 \"这是消息1\" \n" ;
echo
" $argv[0] recv 1 \n" ;
echo
" $argv[0] rem 1 \n" ;
echo
" $argv[0] dele \n" ;
exit;
}

// $SHMKey = ftok(__FILE__, "Z");
$SHMKey = "123456" ;

## 创建/打开共享内存段
$seg = shm_attach( $SHMKey, 1024, 0666 ) ;

switch (
$argv[1] ) {
case
"send":
shm_put_var($seg, $argv[2], $argv[3]);
echo
"发送消息成功...\n" ;
break;

case
"recv":
$data = shm_get_var($seg, $argv[2]);
echo
$data . "\n" ;
break;

case
"rem":
shm_remove_var($seg, $argv[2]);
break;

case
"dele":
shm_remove($seg);
break;

case
"dele2":
`
/usr/bin/ipcrm -M 123456`;
break;
}
?>
-1
nkatz at yahooo dot com
15 年前
回复 jpeter1978 at yahoo dot com ... 假设一个字符通常占用两个字节,序列化变量(包括数组或对象)的大小为 2 * strlen()。44 比 php at cytrax dot de 提出的 24 + 16 = 40 多 4 个字节,这是因为最坏情况下需要 4 字节对齐。所以,如果您太懒得使用类似 zeppelinux at comcast dot net 中的方法对齐它,那么这个公式可能是一个可靠的选择。

($strlen($serialized_array_or_obj) /4 ) * 4 ) + 40;

zeppelinux 的公式在结尾常量部分会使用 20(对于 4 字节整数 CPU)或 36(对于 64 位或 8 字节 CPU),所以 40 或 44 可能只是为了实现头部填充,但这当然不会造成伤害。
-1
php at cytrax dot de
22 年前
共享内存所需的大小(在 Linux 系统上测试)可以按如下方式计算:

每个想要存储的变量:24 字节
+ 序列化变量大小(见下文),以 4 字节对齐
+ 16 字节

如果使用相同键更新变量,则需要额外的内存来存放旧变量。
-1
lew at persiankitty dot com
23 年前
在 FreeBSD 中查找共享内存内核设置

sys1# sysctl -a | grep -i SHM

kern.ipc.shmmax: 4194304
kern.ipc.shmmin: 1
kern.ipc.shmmni: 96
kern.ipc.shmseg: 64
kern.ipc.shmall: 1024
kern.ipc.shm_use_phys: 0

这表明我们最多可以分配 4GB(哇)的共享内存,等等...
To Top