我在 Debian Lenny 32bit 下运行 Apache2.2 服务器+PHP 5.2。我在 C 中构建了一个“简单”的 php 模块扩展。这个扩展做一件事:通过套接字调用一个小的“回声服务器”并等待结果(在双方发送/接收一个“A”)。
进入prefork-mpm 模型,我将套接字设置为全局 var PHP 结构。所以在全球范围内,扩展源听起来像这样:
在 mymodule.h
ZEND_BEGIN_MODULE_GLOBALS(hello)
int socket;
ZEND_END_MODULE_GLOBALS(hello)
#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif
在 mymodule.c
...
static int sock_create(char *address, int port, SA_IN *servaddr)
{
int s;
s = socket(AF_INET, SOCK_STREAM, 0);
if (!s)
return 0;
memset(servaddr, 0, sizeof(SA_IN));
servaddr->sin_family = AF_INET;
servaddr->sin_port = htons((unsigned short)port);
servaddr->sin_addr.s_addr = inet_addr(address);
return s;
}
int send_recv(int sock, char ch)
{
int r;
char tmp[2];
tmp[0]=ch;
tmp[1]=0;
r = send(sock, 'A', 1, 0);
if (r<=-1)
return -1;
r = recv(sock, tmp, 1, 0);
if (r<=-1)
return -2;
return (int) tmp[0];
}
PHP_FUNCTION(cmd_echo)
{
int item, r=0;
SA_IN addr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &item)==FAILURE)
WRONG_PARAM_COUNT;
if (HELLO_G(sa)==0)
{
HELLO_G(sa) = sock_create("server_ip", 4500, &addr);
r = connect(HELLO_G(sa), (SA *) &addr, sizeof(SA));
send_recv(HELLO_G(sa), 'a');
RETURN_LONG(6);
}
else
{
send_recv(HELLO_G(sa), 'b'); // <---- trouble here, hangs...
RETURN_LONG(7);
}
RETURN_LONG(1);
}
听起来很完美......每次 Apache 分叉一个新进程时,我都会创建一个新连接 (sa==0) 如果 Apache 一段时间后决定“重用”现有进程,我的套接字 - 已经连接 - (并且我敢肯定)将在不创建套接字的情况下将数据发送到服务器。就像某种“套接字池”一样
但麻烦来了……
由于未知原因,在 Apache 尝试重用打开的套接字的情况下,send_recv() 挂起在 send() 命令上。当然,套接字处于阻塞模式,但为什么会出现这种行为?
(顺便说一句,如果我将相同的代码(当然没有 PHP 代码)运行到具有多个 send_recv 的可执行文件中,一切都会完美运行!)
Apache似乎锁定了套接字!??
任何想法 ?