多进程通信的时候,会涉及到共享内存。
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. VS 和Visual Assist X快捷键(转)

    Visual Assist X 最有用的快捷键 1.Alt + G: 在定义与声明之间互跳. 2.Alt + O: 在.h与.cpp之间互跳.(O是字母O,不是数字零) 3.Alt + Shift + ...

  2. DNS被污染后

    如果有条件,自已DNS还是非常必要的,至少有一亩三分地的净土. 但是DNS污染是无处不在的,特别是 Forwarding的记录, 一旦 IPv6 Tunnel连接失败,DNS Server  瞬间就被 ...

  3. Javascript 链式作用域 function fn(){}和var fn=function(){}区别

    其实对于Javascript链式作用域的描述,包括,JS权威指南,都有些太冗长了--但是很准确:JavaScript中的函数运行在他们被定义的作用域里,而不是他们被执行的作用域里. 这句话有点难懂,但 ...

  4. C#统计英文文本中的单词数并排序

    思路如下:1.使用的Hashtable(高效)集合,记录每个单词出现的次数2.采用ArrayList对Hashtable中的Keys按字母序排列3.排序使用插入排序(稳定) public void S ...

  5. TSQL--HASH JOIN

    算法: 将其中一张表的连接列做hash,然后遍历另外一张表,对遍历出的每一行到hash表中匹配查找 要求:hash表不要求表排序或有索引

  6. WPF Viewport3D 解决透视模式时窗体模糊

    最近折腾Viewport3D玩,遇到了一些诡异的问题,研究一下略有心得,特此和大家分享~ 三维图形概述: https://msdn.microsoft.com/zh-cn/library/ms7474 ...

  7. MFC学习(三):项目学习

    1. 概述 MFC程序由CWinApp.MainFrm(含Menu,可用CSplitterWndEx分割).众多Dialog等组成. MFC既可以使用纯Dialog的形式,也可以使用Document+ ...

  8. Mayor's posters 线段树区间覆盖

    题目链接 http://poj.org/problem?id=2528 Description The citizens of Bytetown, AB, could not stand that t ...

  9. Template 动画

    如果设置Template的动画,也就意味着对每一个具有此Template的对象进行动画处理. 比如对ListBoxI的ItemTemplate进行设置,添加动画,触发器等,每一个ListBoxItem ...

  10. 初学python - 使用pip安装扩展库

    cmd pip install -i https://pypi.tuna.tsinghua.edu.cn/simple PyMySQL - 使用清华镜像下载PyMySQL pip python包管理工 ...