多进程通信的时候,会涉及到共享内存。
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. DLL用def定义文件来导出重载函数(转)

    动态链接库DLL_Sample.dll DLL_Sample.h:#ifdef TEST_API# define TEST_API _declspec(dllexport)#else# define ...

  2. vc和halcon数据的相互赋值

    // HTuple→VC 数据类型转换 HTuple hTuple = 1234; int i = hTuple[0].I(); // i=1234 long l = hTuple[0].L(); / ...

  3. 神器与经典--sp_helpIndex

    ======================================================= 每每和那些NB的人学习技术的时候,往往都佩服他们对各个知识点都熟捻于心,更佩服的是可以在 ...

  4. 如何使用jQuery写一个jQuery插件

    jQuery插件其实是前端框架的思维,构成一个框架,个人认为必须满足以下几个基础条件:1. 可重用,2. 兼容性,3. 维护方便,虽说现在有很多比较成熟的前端框架,但是也有部分存在配置麻烦,学习成本大 ...

  5. 24. 两两交换链表中的节点 leetcode

    题目: 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例: 给定 1->2->3->4, 你应该返回 ...

  6. linux 修改主机名 【root@主机名 ~】

    centos 7修改方式: hostnamectl  set-hostname   hostuser reboot 或者直接vi /etc/hostname 添加内容: hostuser 检查修改效果 ...

  7. navicat 导入txt到数据库

    一直都是建库 一条条添加数据 昨天碰上要导入一堆数据突然不知所措 查资料才弄好,先记录一下,以免忘记 这个是导入的数据名可以改 对导入数据表进行增删改 以上!

  8. 爬虫开发9.scrapy框架之递归解析和post请求

    今日概要 递归爬取解析多页页面数据 scrapy核心组件工作流程 scrapy的post请求发送 今日详情 1.递归爬取解析多页页面数据 - 需求:将糗事百科所有页码的作者和段子内容数据进行爬取切持久 ...

  9. Elasticsearch学习(3) spring boot整合Elasticsearch的原生方式

    前面我们已经介绍了spring boot整合Elasticsearch的jpa方式,这种方式虽然简便,但是依旧无法解决我们较为复杂的业务,所以原生的实现方式学习能够解决这些问题,而原生的学习方式也是E ...

  10. React Native 搭建开发环境

    1.先安装node.js,https://nodejs.org/en/download/ 然后,双击下载好的.msi文件安装即可,安装完成后,打开终端,输出npm -v 即可查看我们刚才安装的node ...