达达道长 360云计算 


女主宣言:


分布式、CAP理论、高可用、最终一致性、强一致性、因果一致性、异地多活... 云云听多了,开发 GG 这次给大家带来的是一篇单机共享内存的使用场景和分析,刷新你的胃口!!!有木有!!!

PS:丰富的一线技术、多元化的表现形式,尽在“HULK一线技术杂谈”,点关注哦!


使用场景 监控汇总

目前正在用的一个场景,针对某一台机器上的错误进行汇总并报警,我们把一分钟之内的相同报警合并成一条,用共享内存来暂存,非常实用且高效。


 PHP SESSION

如果你是单机的服务,且又启用了session,那么可以把session换成共享内存的来存储,会比文件要快上不少,这里还要强调是单机,这是最大的软肋,但就功能上来讲没有memcache方便。


什么是共享内存


共享内存是一种在同一台机器的不同进程(应用程序)之间交换数据的方式。一个进程可创建一个可供其他进程访问的内存段,并赋予它相应的权限。每个内存段拥有一个惟一的ID,我们通常称之为shmid,这个ID指向一个物理内存区域,其他进程可通过此ID来操作这块内存, 包扩读取、写入以及删除。


共享内存的使用是一种在进程之间交换数据的快速方法,主要因为在创建内存段之后传递数据,不会涉及内核。这种方法常常称为进程间通信 (IPC)。其他 IPC 方法包括管道、消息队列、RPC 和套接字。


 PHP 中几种常见的共享内存使用方式

APC 可以缓存 PHP 的 opcode 提高应用的性能,可以在同个 PHP-FPM 进程池的进程间共享数据,常用功能如下:

apc_store,apc_fetch,apc_add,apc_delete,apc_inc apc_dec,apc_cas,apc_clear_cache,apc_sma_info


Shmop Unix 系统共享内存使用接口常用功能:

shmop_open,shmop_close,shmop_read,shmop_write,shmop_delete


ipcs -m  查看本机共享内存的状态和统计。

ipcrm -m shmid 或 ipcrm -M shmkey 清除共享内存中的数据。


SystemV Shm常用功能:

ftok, shm_attach, shm_detach, shm_put_var, shm_get_var, shm_remove_var。


使用共享内存需要考虑操作的原子性和锁、并行和互斥。
sem 信号量相关函数:sem_get,sem_remove,sem_acquire,sem_release

PHP 提供的 IPC 机制。
--enable-shmop 共享内存,只能按字节操作
--enable-sysvsem 信号量
--enable-sysvshm 共享内存,和 shmop 的差别是提供的操作函数不同,支持 key、value操作
--enable-sysvmsg 消息队列


本文主讲


如何使用  PHP shmop 创建和操作共享内存段,使用它们存储可供其他应用程序使用的数据。


 1. 创建内存段

共享内存函数类似于文件操作函数,但无需处理一个流,您将处理一个共享内存访问 ID。第一个示例就是 shmop_open 函数,它允许您打开一个现有的内存段或创建一个新内存段。此函数非常类似于经典的 fopen 函数,后者打开用于文件操作的流,返回一个资源供其他希望读取或写入该打开的流的函数使用。让我们看看 shmop_open的用法:

<?php$key = ftok(__FILE__, 'h');$mode = 'c';$permissions = 0644;$size = 1024;$shmid = shmop_open($key, $mode, $permissions, $size);?>


第一个参数($key):

系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个key值。通常情况下,该key值通过ftok函数得到, * *key是一个我们逻辑上表示共享内存段的标识。不同进程只要选择同一个Key值就可以共享同一段存储段。


第二个参数($mode):

访问模式,它类似于fopen的访问模式,有以下几种
模式 “a”,它允许您访问只读内存段
模式 “w”,它允许您访问可读写的内存段
模式 “c”,它创建一个新内存段,或者如果该内存段已存在,尝试打开它进行读写
模式 “n”,它创建一个新内存段,如果该内存段已存在,则会失败,返回 false,并伴随有warning: unable to attach or create shared memory segment


第三个参数($permissions):

内存段的权限。您必须在这里提供一个八进制值,它类似于UNIX操作系统文件和目录的操作权限。


第四个参数($size):

内存段大小,以字节为单位。在写入一个内存段之前,您必须在它之上分配适当的字节数。


返回结果:

此函数返回一个 ID 编号,其他函数可使用该 ID 编号操作该共享内存段。这个 ID 是共享内存访问 ID,与系统 ID 不同,它以参数的形式传递。请注意不要混淆这两者。如果失败,shmop_open 将返回 FALSE。


shmop_open成功后,使用ipcs -m, 可以查看到刚刚创建的内存段,注意 申请的内存段有严格的权限,比如用root用户申请的,普通用户就无权访问

[[test@test]~/temp\]$ ipcs -m------ Shared Memory Segments --------key shmid owner perms bytes nattch status0x00924660 0 root 666 8 1040x00000000 294915 root 666 787528456 1 dest0x68410e9f 753668 test 644 1024 0


 2. 向内存段写入数据

使用 shmop_write 函数向共享内存块写入数据。此函数的使用很简单,它仅接受 3 个参数,如下所示。

<?php
//这里shmid可以延用上一段代码返回的shmid
$shmid = shmop_open(ftok(__FILE__,'h), 'c', 0644, 1024);shmop_write($shmid, "Hello World!", 0);?>


这个函数类似于 fwrite 函数, 在这里有三个参数。

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

第二个参数($data):是您希望存储的数据。
第三个参数($offset):是您希望开始写入的位置。默认情况下,我们始终使用 0 来表示开始写入的位置。

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


 3. 从内存段读取数据

从共享内存段读取数据很简单。您只需要一个打开的内存段和 shmop_read 函数,它接受三个参数,如下所示:

<?php$shmid = shmop_open(ftok(\__FILE_\_,'h), 'c', 0644, 1024);shmop_write($shmid, "Hello World\!", 0);var_dump(shmop_read($shmid, 0, 11));?>

第一个参数($shmid):是 shmop_open 返回的 ID,它识别您操作的共享内存块。
第二个参数($start):是您希望从内存段读取的位置,这个参数可以始终为0, 表示数据的开头
第三个参数($count):是您希望读取的字节数。一般情况下我们用shmop_size($shmid),以便完整的读取它。


 4. 删除内存段

shmop_delete 该函数只接收一个参数,如下所示:

<?php$shmid = shmop_open(ftok(\__FILE_\_,'h), 'c', 0644, 1024);shmop_delete($shmid);?>

其实这个函数不会实际删除该内存段。它将该内存段标记为删除状态,因为共享内存段在有其他进程正在使用它时无法被删除。shmop_delete 函数将该内存段标记为删除,阻止任何其他进程打开它。要删除它,我们需要关闭该内存段。


 5. 关闭内存段

打开一个共享内存段会 “附加” 到它。附加该内存段之后,我们可在其中进行读取和写入,但完成操作后,我们必须从它解除。

<?php$shmid = shmop_open(ftok(\__FILE_\_,'h), 'c', 0644, 1024);shmop_write($shmid, "Hello World\!", 0);shmop_delete($shmid);shmop_close($shmid);?>


 共享内存的原子操作 - 信号控制

针对共享内存的写操作本身不是原子性的,那么当我们大量并发进行读写的时候,怎么保证原子性呢,这里要引入信号量进行控制。


PHP 也提供了内置扩展 sysvsem ,其实我们在看sysvsem 提供的一系列sem_*的方法的时候,就会想到,这和上面提到的shmop_*有什么区别呢,我们来看官房文档中的这一个解释:PHP already had a shared memory extension (sysvshm) written by Christian Cartus <cartus@atrior.de>, unfortunately this extension was designed with PHP only in mind and offers high level features which are extremely bothersome for basic SHM we had in mind.
也就是说:sysvshm 扩展提供的方法在存储之前对用户的数据进行serialize处理,这里就导致这个存储的数据是无法与其它语言共享的,这一系列方法是php only的方法。


引入信号控制之后的示例:

<?php$key = ftok(_FILE_, 'h')$mode = "c";&nbsp;$permissions = 0755;&nbsp;$size = 1024; // 内存段的大小,单位是字节$key = ftok(\__FILE_\_, 'h');$semid = sem_get($key);# 请求信号控制权if (sem_acquire($semid)) {$shmid = shmop_open($key, 'c', 0644, 1024);# 读取并写入数据shmop_write($shmid, '13800138000', 0);# 关闭内存块shmop_close($shmid);# 释放信号sem_release($semid);}


共享内存的操作是非常快的,在本地想要模拟实现写入冲突是非常困难的,但是本地想模拟实现写入冲突实际上是非常难的(考虑到计算机的执行速度)。在本地测试中,使用 for 循环操作时如果不使用shmop_close 关闭资源会出现无法打开共享内存的错误警告。这应该是因为正在共享内存被上一次操作占用中还没有释放导致。


共享内存,memcache,文件的读写速度对比。

以下是同时读写1k的数据读写100000次的时间对比:

 读(s)写(s)
memcache7.88.11
文件2.63.2
shm0.10.07 


共享内存的使用场景,想了很久,结合自己曾经做过的项目,发现能用到共享内存的地方比较少。


©著作权归作者所有:来自51CTO博客作者mob604756f04b77的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. Redis 内存为什么不宜过大
  2. 诊断虚拟机频繁 OOM 的问题
  3. 【Java内存溢出】系列(2/8):GC overhead limit exceeded
  4. MySQL:表级锁、行级锁、共享锁、排他锁、乐观锁、悲观锁
  5. 一个Java对象究竟占用多大内存? --Java性能优化基础
  6. nginx负载均衡中的session共享
  7. hive的join优化
  8. 智汇华云 | ArcherOS Stack共享存储虚拟化技术剖析
  9. 手机内存不足应该怎么解决

随机推荐

  1. Android木马之“妄想”
  2. [Android][转]Android(安卓)View绘制13问
  3. 这是一份全面 & 详细的Android学习指南
  4. Android屏幕适配终极方案-原理篇
  5. android小说阅读、MVP + RxJava + Retrof
  6. achartengine在Android中ScrollView组件
  7. 修改Android工程版本
  8. 【Android TextView/EditText 不允许换行
  9. Unity打包到android中屏蔽权限弹窗
  10. 为什么说Android令人沮丧!?