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
没有读权限。ENOENT
:pathname
指定的文件或目录不存在。ENOMEM
:没有足够的内存来执行操作。EINVAL
:pathname
不指向一个文件或目录。
注意事项
ftok
生成的键是系统范围内的,这意味着它可能在不同的进程或机器上生成相同的键,如果它们使用了相同的pathname
和proj_id
。因此,要确保在系统的不同部分使用不同的pathname
或proj_id
。- 虽然
ftok
通常用于 IPC 机制,但生成的键也可以在其他上下文中使用,只要这些上下文可以处理相同的键空间。 ftok
的使用在某些现代系统上可能被视为过时,特别是在那些提供更强大、更灵活的 IPC 机制的系统上。然而,对于需要兼容旧代码或特定环境的场景,它仍然是一个有效的工具。
3.1.2 shmop_close函数(作废)
- shmop_close — Close shared memory block — 关闭共享内存块
在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