图1是enableCoroutine在php生命周期中的大概位置,关于php生命周期详细的展示可以参考https://www.jianshu.com/p/03db25507360
void swoole_runtime_init(int module_number)
{
//类初始化参数依次为 (模块,命名空间,链式命名,短名称,方法集合)
SWOOLE_INIT_CLASS_ENTRY(swoole_runtime, "Swoole\\Runtime", "swoole_runtime", NULL, swoole_runtime_methods);
//序列化
SWOOLE_SET_CLASS_SERIALIZABLE(swoole_runtime, zend_class_serialize_deny, zend_class_unserialize_deny);
SWOOLE_SET_CLASS_CLONEABLE(swoole_runtime, zend_class_clone_deny);
SWOOLE_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_runtime, zend_class_unset_property_deny);
//定义常量
SWOOLE_DEFINE(HOOK_FILE);
SWOOLE_DEFINE(HOOK_SLEEP);
SWOOLE_DEFINE(HOOK_TCP);
SWOOLE_DEFINE(HOOK_UDP);
SWOOLE_DEFINE(HOOK_UNIX);
SWOOLE_DEFINE(HOOK_UDG);
SWOOLE_DEFINE(HOOK_SSL);
SWOOLE_DEFINE(HOOK_TLS);
SWOOLE_DEFINE(HOOK_BLOCKING_FUNCTION);
SWOOLE_DEFINE(HOOK_ALL);
}
static const zend_function_entry swoole_runtime_methods[] =
{
//注册enableStrictMode方法谷歌翻译为启用严格模式
PHP_ME(swoole_runtime, enableStrictMode, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
//注册enableCoroutine方法 启用携程
PHP_ME(swoole_runtime, enableCoroutine, arginfo_swoole_runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_FE_END
};
以上就是swoole中runtime模块的初始化的简单介绍,下面是具体执行阶段的enableCoroutine方法调用
enum sw_coro_hook_type
{
//1u的u是后缀,表示unsigned int型常量
SW_HOOK_FILE = 1u << 1,//10
SW_HOOK_SLEEP = 1u << 2,//100
SW_HOOK_TCP = 1u << 3,//1000
SW_HOOK_UDP = 1u << 4,//10000
SW_HOOK_UNIX = 1u << 5,//100000
SW_HOOK_UDG = 1u << 6,//1000000
SW_HOOK_SSL = 1u << 7,//10000000
SW_HOOK_TLS = 1u << 8,//100000000
SW_HOOK_BLOCKING_FUNCTION = 1u << 9,//1000000000
SW_HOOK_ALL = 0x7fffffff,//1111111111111111111111111111111 32个1
};
static PHP_METHOD(swoole_runtime, enableCoroutine)
{
zend_bool enable = 1;//启用,连接上下文应该是启用携程
zend_long flags = SW_HOOK_ALL;//标识,看字面意思和上面定义应该是开启所有钩子
//如果解析参数不是bool或者long则return false
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|bl", &enable, &flags) == FAILURE)
{
RETURN_FALSE;
}
//如果启用
if (enable)
{
if (hook_init)//如果hook_init为真则return false,这里hook_init配置为false
{
RETURN_FALSE;
}
//启用钩子
PHPCoroutine::enable_hook(flags);
}
else//不启用
{
if (!hook_init)//如果hook_init不为真则return false,这里hook_init配置为false
{
RETURN_FALSE;
}
PHPCoroutine::disable_hook();
}
}
bool PHPCoroutine::enable_hook(int flags)
{
//likely和unlikely是一样的,但是实际上执行是不同的,加likely的意思是value的值为真的可能性更大一些,那么执行if的机会大,而unlikely表示value的值为假的可能性大一些,执行else机会大一些。加上这种修饰,编译成二进制代码时likely使得if后面的执行语句紧跟着前面的程序,unlikely使得else后面的语句紧跟着前面的程序,这样就会被cache预读取,增加程序的执行速度
if (unlikely(enable_strict_mode))//这里enable_strict_mode设置为false
{
swoole_php_fatal_error(E_ERROR, "unable to enable the coroutine mode after you enable the strict mode.");
}
if (hook_init)//这里hook_init为false,更上面保持一样的逻辑
{
return false;
}
hook_flags = flags;//赋值
hook_init = true;//设置
//获取php xport流的HashTable
HashTable *xport_hash = php_stream_xport_get_hash();
if (flags & SW_HOOK_FILE)//与运算 满足条件进行内存拷贝
{
memcpy((void*) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper));
memcpy((void*) &php_plain_files_wrapper, &sw_php_plain_files_wrapper, sizeof(php_plain_files_wrapper));
}
/*“与运算”的特殊用途:
//(1)清零。如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零
//(2)取一个数中指定位方法:找一个数,对应X要取的位,该数的对应位为1,其余位为零,此数与X进行“与运算”可以得到X中的指定位
//下面用的第二种方法,1111111111111111111111111111111 & 10 = 10
*/
if (flags & SW_HOOK_SLEEP)//与运算 满足条件进行函数指针替换
{
//判断EG(function_table)是否存在sleep函数
ori_sleep = (zend_function *) zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("sleep"));
if (ori_sleep)//如果存在进行替换
{
ori_sleep_handler = ori_sleep->internal_function.handler;
ori_sleep->internal_function.handler = PHP_FN(_sleep);
}
//判断EG(function_table)是否存在usleep函数
ori_usleep = (zend_function *) zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("usleep"));
if (ori_usleep)//如果存在进行替换
{
ori_usleep_handler = ori_usleep->internal_function.handler;
ori_usleep->internal_function.handler = PHP_FN(_usleep);
}
//判断EG(function_table)是否存在time_nanosleep函数
ori_time_nanosleep = (zend_function *) zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("time_nanosleep"));
if (ori_time_nanosleep)//如果存在进行替换
{
ori_time_nanosleep_handler = ori_time_nanosleep->internal_function.handler;
ori_time_nanosleep->internal_function.handler = PHP_FN(_time_nanosleep);
}
//判断EG(function_table)是否存在time_sleep_until函数
ori_time_sleep_until = (zend_function *) zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("time_sleep_until"));
if (ori_time_sleep_until)//如果存在进行替换
{
ori_time_sleep_until_handler = ori_time_sleep_until->internal_function.handler;
ori_time_sleep_until->internal_function.handler = PHP_FN(_time_sleep_until);
}
//判断EG(function_table)是否存在stream_select函数
ori_stream_select = (zend_function *) zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("stream_select"));
if (ori_stream_select)//如果存在进行替换
{
ori_stream_select_handler = ori_stream_select->internal_function.handler;
ori_stream_select->internal_function.handler = PHP_FN(_stream_select);
}
}
if (flags & SW_HOOK_BLOCKING_FUNCTION)
{
//判断EG(function_table)是否存在gethostbyname函数
ori_gethostbyname = (zend_function *) zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("gethostbyname"));
if (ori_gethostbyname)//如果存在进行替换
{
ori_gethostbyname_handler = ori_gethostbyname->internal_function.handler;
ori_gethostbyname->internal_function.handler = PHP_FN(swoole_coroutine_gethostbyname);
}
}
if (flags & SW_HOOK_TCP)
{
//判断xport_hash,根据类型翻译像是流工厂?是否存在tcp 并存入ori_factory中联系上下文意思是保存tcp的原始指针,猜测后续可能会用到(还原?直接调用?)
ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tcp"));
//创建一个tcp的socket创建到php xport流中
php_stream_xport_register("tcp", socket_create);
}
if (flags & SW_HOOK_UNIX)
{
ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("unix"));
php_stream_xport_register("unix", socket_create);
}
if (flags & SW_HOOK_UDG)
{
ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udg"));
php_stream_xport_register("udg", socket_create);
}
if (flags & SW_HOOK_UDP)
{
ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udp"));
php_stream_xport_register("udp", socket_create);
}
#ifdef SW_USE_OPENSSL
if (flags & SW_HOOK_SSL)
{
ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("ssl"));
php_stream_xport_register("ssl", socket_create);
}
if (flags & SW_HOOK_TLS)
{
ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tls"));
php_stream_xport_register("tls", socket_create);
}
#endif
return true;
}
static struct
{
php_stream_transport_factory tcp;
php_stream_transport_factory udp;
php_stream_transport_factory _unix;
php_stream_transport_factory udg;
#ifdef SW_USE_OPENSSL
php_stream_transport_factory ssl;
php_stream_transport_factory tls;
#endif
} ori_factory = {
nullptr,
nullptr,
nullptr,
nullptr,
#ifdef SW_USE_OPENSSL
nullptr,
nullptr,
#endif
};
//创建socket
static php_stream *socket_create(
const char *proto, size_t protolen, const char *resourcename, size_t resourcenamelen,
const char *persistent_id, int options, int flags, struct timeval *timeout, php_stream_context *context
STREAMS_DC
)
{
php_stream *stream = NULL;
Socket *sock;
if (unlikely(SwooleG.main_reactor == nullptr || !Coroutine::get_current()))
{
return php_socket_create(proto, protolen, resourcename, resourcenamelen, persistent_id, options, flags, timeout, context STREAMS_CC);
}
if (strncmp(proto, "unix", protolen) == 0)
{
sock = new Socket(SW_SOCK_UNIX_STREAM);
}
else if (strncmp(proto, "udp", protolen) == 0)
{
sock = new Socket(SW_SOCK_UDP);
}
else if (strncmp(proto, "udg", protolen) == 0)
{
sock = new Socket(SW_SOCK_UNIX_DGRAM);
}
#ifdef SW_USE_OPENSSL
else if (strncmp(proto, "ssl", protolen) == 0 || strncmp(proto, "tls", protolen) == 0)
{
sock = new Socket(SW_SOCK_TCP);
sock->open_ssl = true;
}
#endif
else
{
sock = new Socket(SW_SOCK_TCP);
}
if (UNEXPECTED(sock->socket == nullptr))
{
_failed:
swoole_php_fatal_error(E_WARNING, "new Socket() failed. Error: %s [%d]", strerror(errno), errno);
delete sock;
return NULL;
}
if (FG(default_socket_timeout) > 0)
{
sock->set_timeout((double) FG(default_socket_timeout));
}
php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t*) emalloc(sizeof(*abstract));
memset(abstract, 0, sizeof(*abstract));
abstract->socket = sock;
abstract->stream.timeout.tv_sec = FG(default_socket_timeout);
abstract->stream.socket = sock->get_fd();
abstract->read_timeout = (double) FG(default_socket_timeout);
persistent_id = nullptr;//prevent stream api in user level using pconnect to persist the socket
stream = php_stream_alloc_rel(&socket_ops, abstract, persistent_id, "r+");
if (stream == NULL)
{
goto _failed;
}
return stream;
}
网友评论