美文网首页猿学习
swoole源码学习-一键协程化的enableCoroutine

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

作者: IBoya | 来源:发表于2019-05-12 17:28 被阅读53次
    图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;
    }
    

    相关文章

      网友评论

        本文标题:swoole源码学习-一键协程化的enableCoroutine

        本文链接:https://www.haomeiwen.com/subject/hjocaqtx.html