多进程通信的时候,会涉及到共享内存。
shmop_open()
创建或打开一个内存块

PHP_FUNCTION(shmop_open)
{
long key, mode, size;
struct php_shmop *shmop;
struct shmid_ds shm;
int rsid;
char *flags;
int flags_len;
//解析传PHP进来的参数
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsll", &key, &flags, &flags_len, &mode, &size) == FAILURE) {
return;
} if (flags_len != 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not a valid flag", flags);
RETURN_FALSE;
} //创建一个共享内存块
shmop = emalloc(sizeof(struct php_shmop));
//初始化共享内存块
memset(shmop, 0, sizeof(struct php_shmop)); shmop->key = key;
shmop->shmflg |= mode; switch (flags[0])
{
case 'a':
shmop->shmatflg |= SHM_RDONLY;
break;
case 'c':
shmop->shmflg |= IPC_CREAT;
shmop->size = size;
break;
case 'n':
shmop->shmflg |= (IPC_CREAT | IPC_EXCL);
shmop->size = size;
break;
case 'w':
/* noop
shm segment is being opened for read & write
will fail if segment does not exist
*/
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid access mode");
goto err;
} if (shmop->shmflg & IPC_CREAT && shmop->size < 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Shared memory segment size must be greater than zero");
goto err;
}
//C语言shmget(得到一个共享内存标识符或创建一个共享内存对象)
shmop->shmid = shmget(shmop->key, shmop->size, shmop->shmflg);
if (shmop->shmid == -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to attach or create shared memory segment");
goto err;
}
//shmctl(共享内存管理) IPC_STAT 获取内存状态
if (shmctl(shmop->shmid, IPC_STAT, &shm)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to get shared memory segment information");
goto err;
}
//shmat(把共享内存块对象映射到调用进程的地址空间) 通俗的来说,用来标识是哪块进程在使用。
shmop->addr = shmat(shmop->shmid, 0, shmop->shmatflg);
if (shmop->addr == (char*) -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to attach to shared memory segment");
goto err;
} shmop->size = shm.shm_segsz;
//将内存块插入到zend资源列表(后续继续说)
rsid = zend_list_insert(shmop, shm_type TSRMLS_CC);
RETURN_LONG(rsid);
err:
efree(shmop);
RETURN_FALSE;
}

解释一下shmget()、shmat()

int shmget(key_t key, size_t size, int shmflg)
得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符 <key>
0(IPC_PRIVATE):会建立新共享内存对象
大于0的32位整数:视参数shmflg来确定操作。通常要求此值来源于ftok返回的IPC键值
<size>
大于0的整数:新建的共享内存大小,以字节为单位
0:只获取共享内存时指定为0
<shmflg>
0:取共享内存标识符,若不存在则函数会报错
IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个消息队列;如果存在这样的共享内存则报错
void *shmat(int shmid, const void *shmaddr, int shmflg)
连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问 msqid
共享内存标识符
shmaddr
指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
shmflg
SHM_RDONLY:为只读模式,其他为读写模式

shmop_read()函数
读取内存的里面的数据

PHP_FUNCTION(shmop_read)
{
long shmid, start, count;
struct php_shmop *shmop;
int type;
char *startaddr;
int bytes;
char *return_string;
//解析出PHP函数传入的参数
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &shmid, &start, &count) == FAILURE) {
return;
} PHP_SHMOP_GET_RES if (start < 0 || start > shmop->size) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "start is out of range");
RETURN_FALSE;
} if (count < 0 || start > (INT_MAX - count) || start + count > shmop->size) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "count is out of range");
RETURN_FALSE;
}
//获取共享内存的地址
startaddr = shmop->addr + start;
bytes = count ? count : shmop->size - start;
//分配一个内存空间
return_string = emalloc(bytes+1);
//开始从指定的位置拷贝数据,
//return_string 是返回值
//startaddr 开始地址
//bytes 要读的字节数
memcpy(return_string, startaddr, bytes);
return_string[bytes] = 0; RETURN_STRINGL(return_string, bytes, 0);
}

shmop_write()函数
往一个内存块里面写数据

PHP_FUNCTION(shmop_write)
{
struct php_shmop *shmop;
int type;
int writesize;
long shmid, offset;
char *data;
int data_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsl", &shmid, &data, &data_len, &offset) == FAILURE) {
return;
} PHP_SHMOP_GET_RES if ((shmop->shmatflg & SHM_RDONLY) == SHM_RDONLY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "trying to write to a read only segment");
RETURN_FALSE;
} if (offset < 0 || offset > shmop->size) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset out of range");
RETURN_FALSE;
}
//判断共享空间是否满足大小,防止写入溢出
writesize = (data_len < shmop->size - offset) ? data_len : shmop->size - offset;
//开始往共享内存里面进行写入
//shmop->addr 是共享内存的地址
//offset 是写入的偏移量
//writesize 写入数据的多少
memcpy(shmop->addr + offset, data, writesize); RETURN_LONG(writesize);
}

由上面的两个函数的源码看来,其实都是共用了一个函数memcpy(),这个函数我们可以在源码里面的main/php.h追踪到。

define memcpy(d, s, n)  bcopy((s), (d), (n))

可以看出,是对C函数bcopy()的封装。
解释bcopy()

原型:void bcopy(const  void  *src,  void  *dest,  int  n)
用法:#include <string.h>
功能:将字符串src的前n个字节复制到dest中。

shmop_size()函数
获取内存块的大小

/* {{{ proto int shmop_size (int shmid)
returns the shm size */
PHP_FUNCTION(shmop_size)
{
long shmid;
struct php_shmop *shmop;
int type; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &shmid) == FAILURE) {
return;
} PHP_SHMOP_GET_RES //直接获取结构体的size值
RETURN_LONG(shmop->size);
}

shmop_delete()函数
删除一个内存块

PHP_FUNCTION(shmop_delete)
{
long shmid;
struct php_shmop *shmop;
int type; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &shmid) == FAILURE) {
return;
} PHP_SHMOP_GET_RES
//本质是对C语言函数的shmctl()的封装
//IPC_RMID 这常量表示删除共享空间
if (shmctl(shmop->shmid, IPC_RMID, NULL)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "can't mark segment for deletion (are you the owner?)");
RETURN_FALSE;
}
RETURN_TRUE;
}

解释一下shmctl()函数

int shmctl(int shmid, int cmd, struct shmid_ds *buf)
<msqid> 共享内存标识符
<cmd>
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、 mode复制到共享内存的shmid_ds结构内
IPC_RMID:删除这片共享内存
<buf> 共享内存管理结构体

shmop_close()
关闭一个内存块

PHP_FUNCTION(shmop_close)
{
long shmid;
struct php_shmop *shmop;
int type; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &shmid) == FAILURE) {
return;
} PHP_SHMOP_GET_RES
//将一个内存块从当前进程的符号表删除
zend_list_delete(shmid); }

php 内存共享shmop源码阅读的更多相关文章

  1. Spark BlockManager的通信及内存占用分析(源码阅读九)

    之前阅读也有总结过Block的RPC服务是通过NettyBlockRpcServer提供打开,即下载Block文件的功能.然后在启动jbo的时候由Driver上的BlockManagerMaster对 ...

  2. LevelDB(v1.3) 源码阅读之 Arena(内存管理器)

    LevelDB(v1.3) 源码阅读系列使用 LevelDB v1.3 版本的代码,可以通过如下方式下载并切换到 v1.3 版本的代码: $ git clone https://github.com/ ...

  3. C#共享内存实例 附源码

    原文 C#共享内存实例 附源码 网上有C#共享内存类,不过功能太简单了,并且写内存每次都从开头写.故对此进行了改进,并做了个小例子,供需要的人参考. 主要改进点: 通过利用共享内存的一部分空间(以下称 ...

  4. SparkConf加载与SparkContext创建(源码阅读一)

    即日起开始spark源码阅读之旅,这个过程是相当痛苦的,也许有大量的看不懂,但是每天一个方法,一点点看,相信总归会有极大地提高的.那么下面开始: 创建sparkConf对象,那么究竟它干了什么了类,从 ...

  5. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是“引导”文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.c ...

  6. 9 DelayQueueEntry 延时队列节点类——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

  7. 【 js 基础 】【 源码学习 】backbone 源码阅读(一)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  8. JDK部分源码阅读与理解

    本文为博主原创,允许转载,但请声明原文地址:http://www.coselding.cn/article/2016/05/31/JDK部分源码阅读与理解/ 不喜欢重复造轮子,不喜欢贴各种东西.JDK ...

  9. mxnet源码阅读笔记之include

    写在前面 mxnet代码的规范性比Caffe2要好,看起来核心代码量也小很多,但由于对dmlc其它库的依赖太强,代码的独立性并不好.依赖的第三方库包括: cub dlpack dmlc-core go ...

随机推荐

  1. Java多线程设计模式(三)

    目录(?)[-] Read-Wirte Lock Pattern Thread-Per-Message Pattern Worker Thread Pattern   Read-Wirte Lock ...

  2. TunnelBroker for EdgeRouter 后记

    最近入手了UBNT EdgeRouter X, 想着用 IPv6在路由上FQ,经过两天折腾,终于正常使用,留下点标记 供后来同学借鉴. TUNNEL的注册和配置,可以完全按这篇文章来: TunnelB ...

  3. ES6——介绍

    什么是ES6? ECMAScript 6.0 (简称ES6)是继ECMAScript 5.1 之后 JavaScript 语言的下一代标准,发布在2015年6月.他的目标,是使得 JavaScript ...

  4. WPF 添加OCX控件

    1. 在计算机上安装OCX控件 2.创建WPF应用程序 3.打开工具箱 4.单击COM 组件选项卡上,选择安装的控件,,然后单击确定,将控件添加到工具箱 5.在解决方案资源管理器,右键单击UserCo ...

  5. Logstash使用grok解析IIS日志

    Logstash使用grok解析IIS日志 1. 安装配置 安装Logstash前请确认Elasticsearch已经安装正确,参见RedHat6.4安装Elasticsearch5.2.0. 下载链 ...

  6. MySql数据库,对varchar类型字段str进行where str=0条件查询时,查询结果是什么

    在用MySQL查询数据的时候,遇到了一个奇怪的问题.用一个varchar类型的字符串str,作为条件与0比较时,会查str不为0的数据. 比如:SELECT id, idnumber from hr_ ...

  7. heap与stack的区别

    java 的内存分为两类,一类是栈内存,一类是堆内存.栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这 ...

  8. XML标签属性

    0. 内容索引 1. XML序列化与反序列化 2. 相关的常用Attribute 3. 相关的全部Attribute 4. XML序列化答疑 5. 高级议题 1. XML序列化与反序列化 // OBJ ...

  9. Mac环境下安装python库时出现ModuleNotFoundError: No module named 'XXX'

    在使用pip3安装爬虫的一些有关库时发现安装成功后,import时发现又找不到,后来发现一个解决方法. 在Anaconda-Navigator里面打开environment里面寻找发现真的没有,这时再 ...

  10. java类的基本结构

    对象依赖于类存在. 分析过程先有对象后有类,开发过程中先有类后有对象. new是为新建对象开辟内存空间的运算符:以类为模板,开辟空间实例化一个对象,并返回该对象的一个引用. 成员变量 直接在类中定义 ...