PHP内核源码阅读过程(一)

最近在阅读PHP的内核,所以把过程记下来。
本人使用cli方式启动php,版本是7.3.3, 调试平台是centos6.5。
cli方式启动的入口位于 sapi/cli/php_cli.c main
1. 函数save_ps_args
这个函数是进来后调用的第一个函数,原型:char** save_ps_args(int argc, char** argv)
其功能是保存命令行参数,检查命令行参数和环境变量。
保存命令行参数:

save_argc = argc; save_argv = argv;

检查命令行参数:
for (i = 0; (non_contiguous_area == 0) && (i < argc); i++) { if (i != 0 && end_of_area + 1 != argv[i]) non_contiguous_area = 1; end_of_area = argv[i] + strlen(argv[i]); }

检查环境变量:
for (i = 0; (non_contiguous_area == 0) && (environ[i] != NULL); i++) { if (end_of_area + 1 != environ[i]) non_contiguous_area = 1; end_of_area = environ[i] + strlen(environ[i]); }

将环境变量保存到新地址,同时备份一个原始内容:
new_environ = (char **) malloc((i + 1) * sizeof(char *)); frozen_environ = (char **) malloc((i + 1) * sizeof(char *)); if (!new_environ || !frozen_environ) goto clobber_error; for (i = 0; environ[i] != NULL; i++) { new_environ[i] = strdup(environ[i]); if (!new_environ[i]) goto clobber_error; } new_environ[i] = NULL; environ = new_environ; memcpy((char *)frozen_environ, (char *)new_environ, sizeof(char *) * (i + 1));

将命令行参数保存到新地址:
char** new_argv; int i; new_argv = (char **) malloc((argc + 1) * sizeof(char *)); if (!new_argv) goto clobber_error; for (i = 0; i < argc; i++) { new_argv[i] = strdup(argv[i]); if (!new_argv[i]) { free(new_argv); goto clobber_error; } } new_argv[argc] = NULL; argv = new_argv;

2. 设置额外的函数
cli_sapi_module.additional_functions = additional_functions;

有三个函数,其定义如下:
static const zend_function_entry additional_functions[] = { ZEND_FE(dl, arginfo_dl) PHP_FE(cli_set_process_title,arginfo_cli_set_process_title) PHP_FE(cli_get_process_title,arginfo_cli_get_process_title) PHP_FE_END };

3. signal(SIGPIPE, SIG_IGN), 忽略SIGPIPE信号。如果对端的socket关闭,会触发SIGPIPE信号,系统默认的处理方式是杀掉这个进程。这里不能被杀掉。
zend_signal_startup初始化信号。
4. 解析命令
while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) { switch (c) { case 'c': if (ini_path_override) { free(ini_path_override); } ini_path_override = strdup(php_optarg); break; case 'n': ini_ignore = 1; break; case 'd': { /* define ini entries on command line */ size_t len = strlen(php_optarg); char *val; if ((val = strchr(php_optarg, '='))) { val++; if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') { ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0")); memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg)); ini_entries_len += (val - php_optarg); memcpy(ini_entries + ini_entries_len, "\"", 1); ini_entries_len++; memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg)); ini_entries_len += len - (val - php_optarg); memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0")); ini_entries_len += sizeof("\n\0\"") - 2; } else { ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0")); memcpy(ini_entries + ini_entries_len, php_optarg, len); memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0")); ini_entries_len += len + sizeof("\n\0") - 2; } } else { ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0")); memcpy(ini_entries + ini_entries_len, php_optarg, len); memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0")); ini_entries_len += len + sizeof("=1\n\0") - 2; } break; } #ifndef PHP_CLI_WIN32_NO_CONSOLE case 'S': sapi_module = &cli_server_sapi_module; cli_server_sapi_module.additional_functions = server_additional_functions; break; #endif case 'h': /* help & quit */ case '?': php_cli_usage(argv[0]); goto out; case 'i': case 'v': case 'm': sapi_module = &cli_sapi_module; goto exit_loop; case 'e': /* enable extended info output */ use_extended_info = 1; break; } }

这里只讲一下 'S' 参数,这个是启动内置的web服务器,所以会载入web服务器有关的一些函数,其 server_additional_functions定义如下:
const zend_function_entry server_additional_functions[] = { PHP_FE(cli_set_process_title,arginfo_cli_set_process_title) PHP_FE(cli_get_process_title,arginfo_cli_get_process_title) PHP_FE(apache_request_headers, arginfo_no_args) PHP_FE(apache_response_headers, arginfo_no_args) PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args) PHP_FE_END };

5. 初始化模块参数
sapi_module->ini_defaults = sapi_cli_ini_defaults; sapi_module->php_ini_path_override = ini_path_override; sapi_module->phpinfo_as_text = 1; sapi_module->php_ini_ignore_cwd = 1;

sapi_cli_ini_defaults是默认ini配置的读取函数,定义如下:
#define INI_DEFAULT(name,value)\ ZVAL_NEW_STR(&tmp, zend_string_init(value, sizeof(value)-1, 1)); \ zend_hash_str_update(configuration_hash, name, sizeof(name)-1, &tmp); \static void sapi_cli_ini_defaults(HashTable *configuration_hash) { zval tmp; INI_DEFAULT("report_zend_debug", "0"); INI_DEFAULT("display_errors", "1"); }

ini_path_override是从参数'c'传入的ini文件路径或目录。

【PHP内核源码阅读过程(一)】接下来看看sapi的启动。

    推荐阅读