c++ sleep函数_《PHP扩展开发》-hook-(hook原来的sleep)

现在,我们进入一个全新的主题,讲解如何替换掉PHP原来那些阻塞的函数。已达到不修改历史代码,就可以直接协程化我们的代码。

我们这篇文章需要实现的方法如下:

StudyRuntimeenableCoroutine()

首先,我们创建两个文件study_runtime.hstudy_runtime.cc

其中,study_runtime.h文件的内容如下:

#ifndef STUDY_RUNTIME_H
#define STUDY_RUNTIME_H

#include "php_study.h"

#endif /* STUDY_RUNTIME_H */

其中,study_runtime.cc文件的内容如下:

#include "study_runtime.h"

然后修改我们的config.m4文件,增加study_runtime.cc为需要编译的源文件:

study_source_file="
    study.cc 
    study_coroutine.cc 
    study_coroutine_util.cc 
    src/coroutine/coroutine.cc 
    src/coroutine/context.cc 
    ${STUDY_ASM_DIR}make_${STUDY_CONTEXT_ASM_FILE} 
    ${STUDY_ASM_DIR}jump_${STUDY_CONTEXT_ASM_FILE} 
    src/socket.cc 
    src/log.cc 
    src/error.cc 
    src/core/base.cc 
    src/coroutine/socket.cc 
    src/timer.cc 
    study_coroutine_channel.cc 
    src/coroutine/channel.cc 
    study_coroutine_socket.cc 
    study_coroutine_server.cc 
    study_runtime.cc
"

然后和往常一样,我们需要先实现PHPStudyRuntime

在文件study_runtime.cc里面,我们实现我们的enableCoroutine方法。首先,定义一下这个方法的参数。目前我们不需要传递参数:

ZEND_BEGIN_ARG_INFO_EX(arginfo_study_runtime_void, 0, 0, 0)
ZEND_END_ARG_INFO()

然后是enableCoroutine方法的大体框架:

extern PHP_METHOD(study_coroutine_util, sleep);
static void hook_func(const char *name, size_t name_len, zif_handler handler);

static PHP_METHOD(study_runtime, enableCoroutine)
{
    hook_func(ZEND_STRL("sleep"), zim_study_coroutine_util_sleep);
}

(后面我们会去实现hook_func这个函数)

这里,我们的意图是把PHP原来的sleep函数替换成StudyCoroutine::sleep这个方法。zim_study_coroutine_util_sleep实际上就是我们在文件study_coroutine_util.cc中定义的PHP_METHOD(study_coroutine_util, sleep)展开后的结果。

然后,收集enableCoroutine到结构体zend_function_entry里面:

static const zend_function_entry study_runtime_methods[] =
{
    PHP_ME(study_runtime, enableCoroutine, arginfo_study_runtime_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
    PHP_FE_END
};

然后创建一个模块初始化函数来注册PHPStudyRuntime

/**
 * Define zend class entry
 */
zend_class_entry study_runtime_ce;
zend_class_entry *study_runtime_ce_ptr;

void study_runtime_init()
{
    INIT_NS_CLASS_ENTRY(study_runtime_ce, "Study", "Runtime", study_runtime_methods);
    study_runtime_ce_ptr = zend_register_internal_class(&study_runtime_ce TSRMLS_CC); // Registered in the Zend Engine
}

然后,我们在study.cc文件的PHP_MINIT_FUNCTION(study)函数里面调用study_runtime_init

PHP_MINIT_FUNCTION(study)
{
    study_coroutine_util_init();
    study_coro_server_init(module_number);
    study_coro_channel_init();
    study_coro_socket_init(module_number);
    study_runtime_init(); // 新增的代码
    return SUCCESS;
}

我们需要在php_study.h里面对study_runtime_init函数进行声明:

void study_coroutine_util_init();
void study_coro_server_init(int module_number);
void study_coro_channel_init();
void study_coro_socket_init(int module_number);
void study_runtime_init(); // 新增的代码

OK,我们完成了StudyRuntime类的注册。

最后,我们需要实现hook_func

static void hook_func(const char *name, size_t name_len, zif_handler new_handler)
{
    zend_function *ori_f = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, name_len);
    ori_f->internal_function.handler = new_handler;
}

代码其实比较简单,就是先去全局的EG(function_table)里面查找sleep名字对应的zend_function,然后把它的handler换成我们新的new_handler即可。也就是说,PHP内核实现的C函数实际上是会以函数指针的形式保存在zend_function.internal_function.handler里面。

然后,我们重新编译、安装扩展:

phpize --clean && phpize && ./configure && make && make install
----------------------------------------------------------------------

Build complete.
Don't forget to run 'make test'.

Installing shared extensions:     /usr/local/lib/php/extensions/no-debug-non-zts-20180731/
Installing header files:          /usr/local/include/php/

OK,编译、安装扩展成功。

我们来编写测试脚本:

<?php

study_event_init();

Sgo(function ()
{
    var_dump(StudyCoroutine::getCid());
    sleep(1);
    var_dump(StudyCoroutine::getCid());
});

Sgo(function ()
{
    var_dump(StudyCoroutine::getCid());
});

study_event_wait();

这个脚本没有开启hook,执行结果如下:

~/codeDir/cppCode/study # php test.php
int(1)
int(1)
int(2)

符合预期。进程因为第一个协程调用了阻塞的sleep函数,所以导致整个进程阻塞了起来,所以打印是顺序的。

我们现在来开启一下hook功能:

<?php

study_event_init();

StudyRuntime::enableCoroutine();

Sgo(function ()
{
    var_dump(StudyCoroutine::getCid());
    sleep(1);
    var_dump(StudyCoroutine::getCid());
});

Sgo(function ()
{
    var_dump(StudyCoroutine::getCid());
});

study_event_wait();

结果如下:

~/codeDir/cppCode/study # php test.php
int(1)
int(2)
int(1)

因为我们已经把PHP原先的sleep函数替换成了StudyCoroutine::sleep方法,所以,进程不会阻塞起来,会在调用sleep之后立马切换到第二个协程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值