小白兔晒黑了

导航

 

1  进程间通信的常见方式

2 共享内存(Shared Memory)

2.1、共享内存的基本概念

优点:共享存储允许两个或多个进程共享一给定的内存区。由于数据不需要在客户机和服务器之间复制,所以这是最快的一种通信方式。

缺点:共享内存无进程间协调机制。这意味着,当多个进程或线程访问和修改同一块共享内存区域时,它们必须自己管理对这块内存的访问,以防止数据冲突和不一致。具体来说,当一个进程(我们称之为写入方)正在向共享内存写入数据时,另一个进程(我们称之为读取方)可能同时尝试读取这块内存。由于共享内存没有内置的同步机制,读取方可能会读取到写入方还未完   全写入的数据,或者读取到写入方写入过程中的中间状态,从而导致数据的不一致性和错误。

2.2 php共享内存有两种方式:System V 与  shmop

PHP做内存共享有两套接口。

一个是shm(System V IPC函数的封装),它实际上是变量共享,会把对象变量序列化后再储存。使用起来倒是挺方便,但是序列化存储对于效率优先的内存访问操作而言就没啥意义了。System V IPC只能在linux下使用,需开启–enable-sysvshm扩展;另外一个是shmop,不过功能上比shm弱了一些,在 Linux 上,这些函数直接是通过调用 shm* 系列的函数实现,而 Winodows 上也通过对系统函数的封装实现了同样的调用。
这两个都无需安装外部库文件。shmop在linux和windows(win2k之后的系统,win98不支持)都可以使用,但在windows的时候,只有在php是ISAPI运行模式才能正常工作,需开启–enable-shmop扩展。

 

3 shmop功能集

3.1 共享内存函数

shmop是一个易于使用的功能集,允许PHP读写创建和删除服务器系统共享内存模块。

3.1.1 ftok函数

ftok函数是Linux系统中提供的一种比较重要的进程间通信机制,它可以将一个已经存在的文件的路径名和一个子序号(通常为非负整数)作为输入,然后返回一个唯一的key_t类型的键值。这个键值在系统中是全局唯一的,可以用于标识和访问特定的IPC结构(如共享内存、消息队列)。
使用ftok函数时,需要确保指定的文件路径下存在一个有效的文件,并且该文件在程序运行期间不会被删除或移动。否则,ftok函数可能会返回错误或生成不同的键值,导致进程间通信失败。
ftok 函数在 Unix 和 Linux 系统中用于生成一个唯一的键(key),这个键通常用于 IPC(进程间通信)机制,如消息队列、信号量或共享内存。它基于给定的文件路径和单个字符(通常是项目的唯一标识符)来生成这个键。ftok 的名字来源于 "file to key" 的缩写。

参数

  • filename:指向文件路径的指针,这个文件通常是项目中的一个已知文件。这个路径不需要指向一个实际存在的文件,但必须是唯一的,以便在不同的项目或实例中生成不同的键。
  • project_id:一个 8 位(1 字节)的字符串,通常用于进一步区分同一路径下的不同键。(比如 a 、b、c)

返回值

如果成功,ftok 函数返回一个唯一的键。如果失败,返回 -1,并设置 errno 以指示错误。

错误处理

如果 ftok 失败,可以检查 errno 来获取更多信息:

  • EACCES:调用进程对 pathname 没有读权限。
  • ENOENTpathname 指定的文件或目录不存在。
  • ENOMEM:没有足够的内存来执行操作。
  • EINVALpathname 不指向一个文件或目录。

注意事项

  • ftok 生成的键是系统范围内的,这意味着它可能在不同的进程或机器上生成相同的键,如果它们使用了相同的 pathname 和 proj_id。因此,要确保在系统的不同部分使用不同的 pathname 或 proj_id
  • 虽然 ftok 通常用于 IPC 机制,但生成的键也可以在其他上下文中使用,只要这些上下文可以处理相同的键空间。
  • ftok 的使用在某些现代系统上可能被视为过时,特别是在那些提供更强大、更灵活的 IPC 机制的系统上。然而,对于需要兼容旧代码或特定环境的场景,它仍然是一个有效的工具。

3.1.2 shmop_close函数(作废)

  • shmop_close — Close shared memory block — 关闭共享内存块
我们在对内存段进行读取和写入,但完成操作后,我们必须从它解除,这非常类似于处理文件时的 fclose 函数。打开包含一个文件的流并在其中读取或写入数据后,我们必须关闭它,否则将发生锁定。
在PHP8.0以后此函数作废

3.1.3 shmop_delete函数

  • shmop_delete — Delete shared memory block — 删除共享内存块

该函数仅接受一个参数:我们希望删除的共享内存 ID,这不会实际删除该内存段。它将该内存段标记为删除,因为共享内存段在有其他进程正在使用它时无法被删除。shmop_delete 函数将该内存段标记为删除,阻止任何其他进程打开它。要删除它,我们需要关闭该内存段。在创建内存块时建议key参数用常量而不用变量,否则很有可能造成内存泄露。

3.1.4 shmop_open函数

  • shmop_open — Create or open shared memory block— 创建或打开共享内存块

第一个参数:是共享内存块的系统 ID。即ftok 函数返回一个唯一的键

第二个参数:是访问模式,它非常类似于 fopen 函数的访问模式。您可以在 4 种不同的模式下访问一个内存段:
a::以只读权限打开;
c:创建新片段,如果已经存在,则已可读写权限打开;
w:以可读写权限打开 ;
n :创建新片段,如果已经存在则失败。对于避免禁态条件有用

第三个参数:希望分配给内存段的权限,这些权限与文档的权限相同。权限需要以八进制形式传递,例如 0644,代表创建者可读可写,组员和其他人可读

第四个参数:提供内存段大小,以字节为单位。由于使用的共享内存片段是固定长度的,在存储和读取的时候要计算好数据的长度,不然可能会写入失败或者读取空值。。
返回:此函数返回一个 ID 编号,其他函数可使用该 ID 编号操作该共享内存段。这个 ID 是共享内存访问 ID,与系统 ID 不同,它以参数的形式传递。请注意不要混淆这两者。如果失败,shmop_open 将返回 FALSE。

在创建内存块时建议key参数用常量而不用变量,否则很有可能造成内存泄露。即用 $shomp_key = ftok(__FILE__,'p');  就成生成一个固定的数字

3.1.5 shmop_read函数

  • shmop_read — Read data from shared memory block —从共享内存块读取数据

从共享内存段读取数据很简单。您只需要一个打开的内存段和 shmop_read 函数。此函数接受一些参数,工作原理类似于 fread。

第一个参数: shmop_open 返回的 ID 
第二个参数:你希望从内存段读取的位置。开始读取的偏移量;必须大于或等于零且小于或等于共享内存段的实际大小。
第三个参数:是您希望读取的字节数。第二个参数可以始终为 0,表示数据的开头,但第三个参数可能存在问题,因为我们不知道我们希望读取多少字节。
这非常类似于我们在 fread 函数中的行为,该函数接受两个参数:打开的流资源(由 fopen 返回)和您希望从该流读取的字节数。使用 filesize 函数(它返回一个文件中的字节数)来完整地读取它。
如果第三个参数设置为0,将自动调用 shmop_size返回内存段数据实际大小

3.1.6 shmop_size函数

  • shmop_size — Get size of shared memory block— 获取共享内存块的大小

shmop_size返回内存段数据实际大小。所谓实际大小,是指:我们开辟了一个长度为100字节的内存空间,但是实际存入的数据长度仅仅90,那么使用shmop_size返回的值就是90.

3.1.7 shmop_write函数

  • shmop_write — Write data into shared memory block— 到共享内存块写入数据

这个函数类似于 fwrite 函数,后者有两个参数:打开的流资源(由 fopen 返回)和您希望写入的数据。shmop_write 函数也执行此任务。

第一个参数:shmop_open 返回的 ID,它识别您操作的共享内存块。

第二个参数:你希望存储的数据,

第三个参数:您希望开始写入的位置。偏移量必须大于或等于零,并且小于或等于共享内存段的实际大小。默认情况下,我们始终使用 0 来表示开始写入的位置。

返回:此函数在失败时会返回 FALSE,在成功时会返回写入的字节数。

3.2 实例

 我们开辟一块内存来统计一下全国的人口:

<?php
//创建键
//__FiLE__ E:\phpClassicExamples\www.aaa.com\第五章\5-06 在进程间共享变量.php
//p 项目标识符 也可以是其他标识符,比如a b c等 但要注意,只能单个字符

$shomp_key = ftok(__FILE__,'p'); //$shomp_key = 1879323013
if($shomp_key==-1){
    die('创建key失败');
}

//创建16384 字节的共享内存块
//shmop_open 创建或打开共享内存块
//0600 希望分配给内存段的权限,这些权限与文档的权限相同。权限需要以八进制形式传递,例如 0644
//成功后,shmop_open() 将返回一个 Shmop 实例,您可以使用它来访问您创建的共享内存段。false 失败时返回

$shmop_id = shmop_open($shomp_key,'c',0600,16384);//$shmop_id = resource(2) of type (shmop) 

if(!$shmop_id ){
    die('创建内存块失败');
}


//取得全部共享内存片段中的数据
//shmop_read  —从共享内存块读取数据
//$shmop_id:由 shmop_open()创建的共享内存块标识符
//第一个0:开始读取的偏移量;必须大于或等于零且小于或等于共享内存段的实际大小。
//第二个0:要读取的字节数;必须大于或等于零,并且 offset 和 size 之和必须小于或等于共享内存段的实际大小。如果设置0 自动读取 shmop_size($shmid) $start 字节。


$population = shmop_read($shmop_id ,0 ,0);//$population = string(16384) "" 

if($population===false){
    die('获取内存片段中的数据失败');
}
//初始化人口数据
$population  = intval($population);
$births =2;//出生人口
$immigrants =1;//移入人口
$deaths =2;//死亡人口
$emigrants=0;//移出人口 
//$population =1;
$population += ($births + $immigrants - $deaths - $emigrants);

//将生成的数据回写入共享内存片段
//$population 要写入共享内存块的字符串
//0 指定在共享内存段内开始写入数据的位置。偏移量必须大于或等于零,并且小于或等于共享内存段的实际大小。
//返回写入数据的大小。


//$population= 'aaa';//用于初始化内存块
$shmop_bytes_written = shmop_write($shmop_id,$population,0);
if($shmop_bytes_written != strlen($population)){
    echo '无法写入全部的人口';
}
shmop_delete($shmop_id);
var_dump($population);
?>

 

3.3 Linux命令

ipcs -m
root@bogon ~]# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 0              gdm              600                 393216            2         dest         
0x00000000 32769             gdm              600                 393216            2         dest                              
0x4337b101 884750           nobody         644                 1024                0

 

说明

key :共享内存的唯一的key值,共享内存通过该key来判断你读取的是哪一块内存。
shmid:当使用key来获取内存时,你获得的是这个id的值。它作为你操作内存块的标识。
owner:创建该共享内存块的用户
perms:该共享内存的读写权限,8禁止,可以是777,与文件的读写权限一致。
bytes:该内存块的大小
nattch:连接该内存块的进程数
status:当前状态,如:dest,即将删除等。

4 System V 

Semaphore 函数

  • ftok — Convert a pathname and a project identifier to a System V IPC key — 将路径名和项目标识符转换为 System V IPC 密钥
  • msg_get_queue — Create or attach to a message queue  — 创建或附加到消息队列
  • msg_queue_exists — Check whether a message queue exists— 检查消息队列是否存在
  • msg_receive — Receive a message from a message queue — 从消息队列接收消息
  • msg_remove_queue — Destroy a message queue — 销毁消息队列
  • msg_send — Send a message to a message queue— 向消息队列发送消息
  • msg_set_queue — Set information in the message queue data structure— 在消息队列数据结构中设置信息
  • msg_stat_queue — Returns information from the message queue data structure— 从消息队列数据结构返回信息
  • sem_acquire — Acquire a semaphore — 获取信号量
  • sem_get — Get a semaphore id— 获取信号量 ID
  • sem_release — Release a semaphore — 释放信号量
  • sem_remove — Remove a semaphore — 删除信号量
  • shm_attach — Creates or open a shared memory segment— 创建或打开共享内存段
  • shm_detach — Disconnects from shared memory segment— 断开与共享内存段的连接
  • shm_get_var — Returns a variable from shared memory— 从共享内存返回变量
  • shm_has_var — Check whether a specific entry exists— 检查特定条目是否存在
  • shm_put_var — Inserts or updates a variable in shared memory— 在共享内存中插入或更新变量
  • shm_remove — Removes shared memory from Unix systems— 从 Unix 系统中删除共享内存
  • shm_remove_var — Removes a variable from shared memory— 从共享内存中删除变量

5 System V

看这里的第七节内容

https://www.cnblogs.com/polax/p/18470612

参考

php手册 : https://www.php.net/manual/zh/ref.sem.php

 

https://www.cnblogs.com/lishuaige/p/5428772.html

https://blog.csdn.net/weixin_73494835/article/details/138870819

System V IPC(进程间通信)机制详解 https://blog.csdn.net/weixin_73494835/article/details/138870819

PHP经典实例学习》之变量学习笔记https://blog.csdn.net/linxury520/article/details/40478977

 

posted on 2024-09-03 23:04  小白兔晒黑了  阅读(35)  评论(0编辑  收藏  举报