首发原文链接:深入理解 Swoole 的底层加载原理

PHP 扩展加载

我们从 php-src/sapi/cli/php_cli.c:1159 文件的入口函数 int main(int argc, char *argv[]) 开始分析。

大家可以先看下面这张图,描述了整个关键函数的加载、调用流程。从模块的初始化,到最后资源的释放回收整个流程,都体现在其中了。「如果图片看不清,可以在文章末尾点击 阅读原文 查看原图」

Swoole 入口文件

我们先看下 Swoole 源码中对入口文件的定义,接下来我们再看在 PHP 中对扩展定义的结构体。

// swoole-src/php_swoole.h:40
extern zend_module_entry swoole_module_entry; // Swoole扩展的模块入口
#define phpext_swoole_ptr &swoole_module_entry PHP_MINIT_FUNCTION(swoole); // 在PHP启动时被调用,用于初始化模块的全局状态。
PHP_MSHUTDOWN_FUNCTION(swoole); // 在PHP关闭时被调用,用于清理和释放模块的全局资源。
PHP_RINIT_FUNCTION(swoole); // 每个PHP请求开始时被调用,用于初始化每个请求的相关资源。
PHP_RSHUTDOWN_FUNCTION(swoole); // 在每个PHP请求结束时被调用,用于释放每个请求的相关资源。
PHP_MINFO_FUNCTION(swoole); // 定义了模块信息的获取函数。这个函数用于返回关于模块的信息,例如版本号、配置选项等。 // 定义了Swoole扩展的全局变量。这些全局变量可以在整个扩展中访问,用于存储一些配置选项和状态信息。
// clang-format off
ZEND_BEGIN_MODULE_GLOBALS(swoole)
zend_bool display_errors;
zend_bool cli;
zend_bool use_shortname;
zend_bool enable_coroutine;
zend_bool enable_preemptive_scheduler;
zend_bool enable_library;
zend_bool enable_fiber_mock;
long socket_buffer_size;
int req_status;
ZEND_END_MODULE_GLOBALS(swoole)
// clang-format on extern ZEND_DECLARE_MODULE_GLOBALS(swoole);

在 PHP 的 Zend 虚拟解析引擎中,对 PHP 扩展的入口文件,进行了统一的定义。基于 PHP 开发的扩展都得实现这一标准,因此 Swoole 也不例外。

// php-src/Zend/zend_modules.h:71
struct _zend_module_entry {
.... // PHP 引擎加载模块时需要执行的函数
zend_result (*module_startup_func)(INIT_FUNC_ARGS);
// PHP 引擎关闭模块时需要执行的函数
zend_result (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
// 每个请求开始时需要执行的函数
zend_result (*request_startup_func)(INIT_FUNC_ARGS);
// 每个请求结束时需要执行的函数
zend_result (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
// 执行 phpinfo 函数时需要显示的模块信息
void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); ....
};

我们通过这张图来看下对应关系。

下面我们对逐个函数进行分析。

PHP_MINIT_FUNCTION

如上述所说,这个函数主要是定义了大量的全局常量、及一些子模块的初始化工作。

// Swoole-src/ext-src/php_swoole.c:369
PHP_MINIT_FUNCTION(swoole) {
// 定义常量
ZEND_INIT_MODULE_GLOBALS(swoole, php_swoole_init_globals, nullptr);
REGISTER_INI_ENTRIES(); // clang-format off
// MUST be on the same line for the inspection tool to recognize correctly
SW_REGISTER_STRING_CONSTANT("SWOOLE_VERSION", SWOOLE_VERSION); ... // 事件循环等子模块的初始化
/** <Sort by dependency> **/
php_swoole_event_minit(module_number);
// base
php_swoole_atomic_minit(module_number); ... SwooleG.fatal_error = fatal_error;
// 设置 Swoole socket 默认缓存区大小
Socket::default_buffer_size = SWOOLE_G(socket_buffer_size);
SwooleG.dns_cache_refresh_time = 60; // 初始化 Zend 引擎字符串资源分配
zend::known_strings_init(); return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION

这个函数的主要是做清扫的作用,清除运行过程中的变量、及关闭相应的资源,避免资源泄露等。

// Swoole-src/ext-src/php_swoole.c:774
PHP_MSHUTDOWN_FUNCTION(swoole) {
// 调用 Zend 引擎释放字符串资源
zend::known_strings_dtor(); // 释放 Swoole 运行过程中资源
php_swoole_runtime_mshutdown();
// 释放 Swoole Websocket 服务资源
php_swoole_websocket_server_mshutdown();
#ifdef SW_USE_PGSQL
php_swoole_pgsql_mshutdown();
#endif #ifdef SW_USE_ORACLE
php_swoole_oracle_mshutdown();
#endif #ifdef SW_USE_SQLITE
php_swoole_sqlite_mshutdown();
#endif // 执行 Swoole 的清理工作
swoole_clean(); return SUCCESS;
}

PHP_RINIT_FUNCTION

每个请求开始需要做的一些初始化工作,主要是针对 Http Server 实现的。

// Swoole-src/ext-src/php_swoole.c:982
PHP_RINIT_FUNCTION(swoole) {
if (!SWOOLE_G(cli)) {
return SUCCESS;
} // 开始请求的初始化工作
SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_BEGIN;
// 将 Swoole 更改为运行状态
SwooleG.running = 1; php_swoole_register_shutdown_function("swoole_internal_call_user_shutdown_begin"); ... // 初始化 Swoole 的 Http Server 等模块
php_swoole_http_server_rinit();
php_swoole_coroutine_rinit();
php_swoole_runtime_rinit();
#ifdef SW_USE_ORACLE
php_swoole_oracle_rinit();
#endif // 结束请求的初始化工作
SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_END; return SUCCESS;
}

PHP_RSHUTDOWN_FUNCTION

针对请求资源进行释放,并及时关闭相应的连接资源。

// Swoole-src/ext-src/php_swoole.c:1018
PHP_RSHUTDOWN_FUNCTION(swoole) {
if (!SWOOLE_G(cli)) {
return SUCCESS;
} // 开始请求的清理工作
SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_BEGIN; rshutdown_callbacks.execute(); // 释放 Swoole 事件循环资源等
swoole_event_free(); php_swoole_server_rshutdown();
php_swoole_async_coro_rshutdown();
php_swoole_redis_server_rshutdown();
php_swoole_coroutine_rshutdown();
php_swoole_coroutine_scheduler_rshutdown();
php_swoole_runtime_rshutdown(); // 清理 Swoole 进程相关的资源
php_swoole_process_clean(); // 更改 Swoole 的运行状态
SwooleG.running = 0;
// 结束请求的清理工作
SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; ... return SUCCESS;
}

PHP_MINFO_FUNCTION

这个最好理解了,就是打印这个扩展的信息,例如:Swoole 的作者、版本等信息。

PHP_MINFO_FUNCTION(swoole) {
char buf[64];
php_info_print_table_start();
php_info_print_table_header(2, "Swoole", "enabled");
php_info_print_table_row(2, "Author", "Swoole Team <team@swoole.com>");
php_info_print_table_row(2, "Version", SWOOLE_VERSION); ... php_info_print_table_end(); DISPLAY_INI_ENTRIES();
}

总结

首先,理解 Swoole 扩展的加载原理,最重要的是要搞懂最开始提到的 PHP 扩展加载全流程。我就是在这个全流程的分析上,花了大量的时间。经常分析到一半,发现逻辑不对,然后就反复的分析其中的关联关系。

其次,对于之前只写 PHP 业务代码,没有接触过 PHP 源代码的人来说,简直就是看天书。因此,如果有志于学习源码的朋友,一定到有足够的耐心,反复研读、琢磨。

最后,Swoole 作为 PHP 在异步通信框架领域的一个重要的扩展,还是值得好好学习的。

深入理解 Swoole 的底层加载原理的更多相关文章

  1. 深入理解React:懒加载(lazy)实现原理

    目录 代码分割 React的懒加载 import() 原理 React.lazy 原理 Suspense 原理 参考 1.代码分割 (1)为什么要进行代码分割? 现在前端项目基本都采用打包技术,比如 ...

  2. 【Docker】7. 镜像-加载原理、分层原理、commit镜像

    一.什么是镜像 镜像是一种轻量级.可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件. 它包含运行某个软件所需的所有内容,包括代码.运行时环境.库.环境变量和配置文件. 所有的应用,直接 ...

  3. iOS 下拉刷新-上拉加载原理

    前言 讲下拉刷新及上拉加载之前先给大家解释UIScrollView的几个属性 contentSize是UIScrollView可以滚动的区域. contentOfinset 苹果官方文档的解释是&qu ...

  4. 老调重弹:JDBC系列之<驱动加载原理全面解析) ----转

      最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读者 ...

  5. Spring Boot源码分析-配置文件加载原理

    在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读.今天让我们继续阅读源码,了解配置文 ...

  6. vue第七单元(vue的单文件组件形式-单文件组件的加载原理-vue-cli构建的开发环境以及生命周期)

    第七单元(vue的单文件组件形式-单文件组件的加载原理-vue-cli构建的开发环境以及生命周期) #课程目标 掌握安装 vue-cli 命令行工具的方法,掌握使用命令行在本地搭建开发环境,使用命令行 ...

  7. composer 实现自动加载原理

    简介 一般在框架中都会用到composer工具,用它来管理依赖.其中composer有类的自动加载机制,可以加载composer下载的库中的所有的类文件.那么composer的自动加载机制是怎么实现的 ...

  8. Vue(基础七)_webpack(webpack异步加载原理)

    ---恢复内容开始--- 一.前言 1.webpack异步加载原理’                                           2.webpack.ensure原理     ...

  9. 深入解析 composer 的自动加载原理 (转)

    深入解析 composer 的自动加载原理 转自:https://segmentfault.com/a/1190000014948542 前言 PHP 自5.3的版本之后,已经重焕新生,命名空间.性状 ...

  10. 解析苹果的官方例子LazyTableImages实现图片懒加载原理

    解析苹果的官方例子LazyTableImages实现图片懒加载原理 首先在官网下载源码: https://developer.apple.com/library/ios/navigation/#sec ...

随机推荐

  1. 直播预告丨Hello HarmonyOS进阶课程第二课——计算机视觉

    为了帮助初识HarmonyOS的开发者快速入门,我们曾推出Hello HarmonyOS系列一共5期课程,从最基础的配置IDE和创建Hello World开始,详细介绍HarmonyOS基础.开发环境 ...

  2. Fatal: (vsim-3381) obsolete library format 解决办法

    有很多软件仿真都是black boxs 黑盒子模式,所以用modelsim提示该错误.错误的原因是在于库用了其他旧的软件版本编译好的. 所以解决的办法如下: 在已经映射好的库选择refresh就可以刷 ...

  3. 容器基础-- namespace,Cgroup 和 UnionFS

    Namespace 什么是 Namespace ? 这里的 "namespace" 指的是 Linux namespace 技术,它是 Linux 内核实现的一种隔离方案.简而言之 ...

  4. Linux基础-01:Linux命令的基本格式

    2.1.1 命令提示符 在CentOS 7操作系统中,Linux命令提示符就像是你与电脑交流的一个小标志,告诉你系统已经准备好接受你的指令了. 它通常会显示在你打开的终端窗口或控制台的最前面. 让我们 ...

  5. 【ASPLOS 2022】机器学习访存密集计算编译优化框架AStitch,大幅提升任务执行效率

    简介: 近日,关于机器学习访存密集计算编译优化框架的论文<AStitch: Enabling A New Multi-Dimensional Optimization Space for Mem ...

  6. 参与 Apache 顶级开源项目的 N 种方式,Apache Dubbo Samples SIG 成立!

    简介: 一说到参与开源项目贡献,一般大家的反应都是代码级别的贡献,总觉得我的代码被社区合并了,我才算一个贡献者,这是一个常见的错误认知.其实,在一个开源社区中有非常多的角色是 non-code con ...

  7. 阿里云 EDAS 3.0 助力唱鸭提升微服务幸福感

    简介: EDAS 3.0 提供的微服务治理,很好的支持了唱鸭 APP 实现微服务应用的发布.监控.管理等日常业务场景.作为运维侧的重要平台和开框架的提供者,EDAS 3.0 帮助用户可以更专注业务.微 ...

  8. dotnet 读 WPF 源代码笔记 简单聊聊文本布局换行逻辑

    在 WPF 里面,带了基础的文本库功能,如 TextBlock 等.文本库排版的重点是在文本的分行逻辑,也就是换行逻辑,如何计算当前的文本字符串到达哪个字符就需要换到下一行的逻辑就是文本布局的重点模块 ...

  9. Typecho博客网站迁移:MySQL ➡️ MarialDB

    目录 1. 引言 2. Typecho的自定义配置迁移 3. 数据库迁移:MySQL- > MarialDB 3.1 在原服务器中备份并导出数据库文件 3.2 将"backupdb.s ...

  10. Linux中的umask

    在Linux中,当创建一个文件或者目录的时候,系统会自动为这个文件或者目录赋予默认的权限,而umask命令就是用来控制这个默认权限的. 查看umask umask的查看有两种方式,一种不带选项-S,一 ...