PHP访问自定义Apache API咨询:内存内数据交互实现方案
嘿,你的需求其实完全可以通过PHP内核扩展实现——你之前觉得扩展做不到可能是个小误解!这种让数据常驻进程内存、直接通过PHP函数读写的场景,正是PHP扩展的拿手好戏,而且完全不需要IPC(比如memcache那种客户端-服务端模式的IPC)。下面给你拆解具体的实现思路、现有案例和实操指引:
先澄清核心误解:PHP扩展完全支持常驻内存数据
PHP扩展的代码是运行在Apache(或其他SAPI)的进程空间里的:
- 如果你用Apache的prefork模式,每个子进程会维护一份独立的内存数据(适合不需要跨进程共享的场景);
- 如果是worker/event线程化模式,可以通过线程安全的全局变量或共享内存实现跨线程共享;
- 要是需要跨所有Apache子进程共享数据,也可以在扩展里直接调用系统共享内存API(比如
shmget/shmat),这属于内核级内存访问,和memcache那种IPC不是一回事。
现有类似实现案例
很多成熟的PHP扩展都是这个思路:
- APC(Alternative PHP Cache):曾经的PHP缓存标杆,提供
apc_store()和apc_fetch()函数,就是把用户数据常驻内存,和你要的SetMyThing/GetMyThing逻辑几乎完全一致(虽然现在APC被OPcache取代,但原理完全可参考); - OPcache:官方的脚本缓存扩展,核心就是把编译后的PHP字节码常驻内存,它的共享内存管理逻辑可以直接借鉴来存储自定义数据;
- 一些专用业务扩展:比如很多电商系统的库存、配置缓存扩展,都是通过自定义扩展把热点数据存在进程内存里,直接用PHP函数读写。
实操实现指引(从零写一个简单扩展)
1. 基础准备
先熟悉PHP扩展开发的基本流程:用phpize生成编译配置、configure指定编译参数、make编译成.so文件,最后在php.ini中加载扩展。如果你要静态编译进PHP/Apache,只需要在编译PHP时把扩展的源码路径加入编译参数即可。
2. 核心代码示例
下面是一个极简的扩展实现,包含SetMyThing和GetMyThing函数,数据常驻进程内存:
#include "php.h" // 定义全局常驻内存变量 static char *g_my_data = NULL; static size_t g_my_data_len = 0; // SetMyThing函数实现 PHP_FUNCTION(SetMyThing) { char *input_data; size_t input_len; // 解析PHP传入的参数 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &input_data, &input_len) == FAILURE) { RETURN_NULL(); } // 释放旧数据,避免内存泄漏 if (g_my_data) { efree(g_my_data); } // 分配新内存并拷贝数据 g_my_data = emalloc(input_len + 1); memcpy(g_my_data, input_data, input_len); g_my_data[input_len] = '\0'; g_my_data_len = input_len; RETURN_TRUE; } // GetMyThing函数实现 PHP_FUNCTION(GetMyThing) { if (g_my_data) { RETURN_STRINGL(g_my_data, g_my_data_len); } else { RETURN_NULL(); } } // 模块初始化函数(进程启动时执行) PHP_MINIT_FUNCTION(my_memory_store) { g_my_data = NULL; g_my_data_len = 0; return SUCCESS; } // 模块销毁函数(进程退出时清理内存) PHP_MSHUTDOWN_FUNCTION(my_memory_store) { if (g_my_data) { efree(g_my_data); g_my_data = NULL; } return SUCCESS; } // 注册PHP函数 static const zend_function_entry my_memory_store_functions[] = { PHP_FE(SetMyThing, NULL) PHP_FE(GetMyThing, NULL) PHP_FE_END }; // 模块定义结构 zend_module_entry my_memory_store_module_entry = { STANDARD_MODULE_HEADER, "my_memory_store", my_memory_store_functions, PHP_MINIT(my_memory_store), PHP_MSHUTDOWN(my_memory_store), NULL, // 请求初始化 NULL, // 请求销毁 NULL, // 模块信息 PHP_VERSION, STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_MY_MEMORY_STORE ZEND_GET_MODULE(my_memory_store) #endif
3. 编译与使用
- 把上述代码保存为
my_memory_store.c,再创建对应的config.m4配置文件(参考PHP扩展开发文档的模板); - 执行
phpize && ./configure --enable-my-memory-store && make && make install; - 在
php.ini中添加extension=my_memory_store.so,重启Apache; - 现在就可以在PHP代码中直接调用:
SetMyThing("hello, persistent memory!"); echo GetMyThing(); // 输出hello, persistent memory!
进阶注意事项
- 线程安全:如果用Apache的worker/event线程化模式,需要把全局变量改成线程特定存储(用
TSRMLS宏或zend_ts_resource),或者加互斥锁避免线程竞争; - 跨进程共享:如果需要所有Apache子进程共享同一份数据,要改用系统共享内存API(
shmget/shmat),同时配合信号量(sem_get)做并发访问控制; - 内存泄漏:一定要在模块销毁函数(
MSHUTDOWN)中释放内存,避免进程长期运行导致内存溢出。
内容的提问来源于stack exchange,提问作者chup
相关产品推荐
相关产品推荐

