猿学习

swoole源码学习-一键协程化的enableCoroutine

2019-05-12  本文已影响53人  IBoya
图1

图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;
}
上一篇 下一篇

猜你喜欢

热点阅读