> 백엔드 개발 > PHP8 > PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1)

PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1)

青灯夜游
풀어 주다: 2023-02-17 12:00:02
앞으로
2777명이 탐색했습니다.

이 기사에서는 PHP8의 기본 커널 소스 코드에 대한 심층 분석을 제공하고 SAPI에 대해 알아봅니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.

PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1)

추천 관련 기사: "PHP8 기반 커널 소스 코드 분석 - 배열(1)"

Docker 아래에 다음 환경 구축

[root@a951700e857d cui-php]# php -v
PHP 8.0.2 (cli) (built: Mar  2 2021 02:40:03) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.2, Copyright (c) Zend Technologies
[root@a951700e857d cui-php]#
로그인 후 복사

Brother Bird 사진 소개

에서 -Zend SAPI(Zend SAPI Internals)에 대한 심층적인 이해

https://link.zhihu.com/?target=https%3A//www.laruence.com/2008/08/12/180.html

PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1)

SAPI(서버 애플리케이션 프로그래밍 인터페이스, 서버 애플리케이션 프로그래밍 인터페이스)는 PHP 외부 환경의 에이전트와 동일합니다. PHP는 터미널에 적용할 수도 있고 웹 서버에 적용할 수도 있습니다. 터미널에 적용하는 SAPI를 CLI SAPI라고 하고, 웹 서버에 적용하는 SAPI를 CGI SAPI라고 합니다.

이전과 다음을 연결하는 중간 레이어나 글루와 같습니다. sapi의 핵심 정의와 매크로 파일은 Structure_sapi_module_struct

//* sapi_module_struct cgi_sapi_module 
static sapi_module_struct cgi_sapi_module = {
	"cgi-fcgi",						/* name */
	"CGI/FastCGI",					/* pretty name */

	php_cgi_startup,				/* startup */
	php_module_shutdown_wrapper,	/* shutdown */

	sapi_cgi_activate,				/* activate */
	sapi_cgi_deactivate,			/* deactivate */

	sapi_cgi_ub_write,				/* unbuffered write */
	sapi_cgi_flush,					/* flush */
	NULL,							/* get uid */
	sapi_cgi_getenv,				/* getenv */

	php_error,						/* error handler */

	NULL,							/* header handler */
	sapi_cgi_send_headers,			/* send headers handler */
	NULL,							/* send header handler */

	sapi_cgi_read_post,				/* read POST data */
	sapi_cgi_read_cookies,			/* read Cookies */

	sapi_cgi_register_variables,	/* register server variables */
	sapi_cgi_log_message,			/* Log message */
	NULL,							/* Get request time */
	NULL,							/* Child terminate */

	STANDARD_SAPI_MODULE_PROPERTIES
};
로그인 후 복사

설명은 다음과 같습니다

PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1)

struct _sapi_module_struct {
	char *name;
	char *pretty_name;

	int (*startup)(struct _sapi_module_struct *sapi_module);
	int (*shutdown)(struct _sapi_module_struct *sapi_module);

	int (*activate)(void);
	int (*deactivate)(void);

	size_t (*ub_write)(const char *str, size_t str_length);
	void (*flush)(void *server_context);
	zend_stat_t *(*get_stat)(void);
	char *(*getenv)(const char *name, size_t name_len);

	void (*sapi_error)(int type, const char *error_msg, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);

	int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers);
	int (*send_headers)(sapi_headers_struct *sapi_headers);
	void (*send_header)(sapi_header_struct *sapi_header, void *server_context);

	size_t (*read_post)(char *buffer, size_t count_bytes);
	char *(*read_cookies)(void);

	void (*register_server_variables)(zval *track_vars_array);
	void (*log_message)(const char *message, int syslog_type_int);
	double (*get_request_time)(void);
	void (*terminate_process)(void);

	char *php_ini_path_override;

	void (*default_post_reader)(void);
	void (*treat_data)(int arg, char *str, zval *destArray);
	char *executable_location;

	int php_ini_ignore;
	int php_ini_ignore_cwd; /* don't look for php.ini in the current directory */

	int (*get_fd)(int *fd);

	int (*force_http_10)(void);

	int (*get_target_uid)(uid_t *);
	int (*get_target_gid)(gid_t *);

	unsigned int (*input_filter)(int arg, const char *var, char **val, size_t val_len, size_t *new_val_len);

	void (*ini_defaults)(HashTable *configuration_hash);
	int phpinfo_as_text;

	char *ini_entries;
	const zend_function_entry *additional_functions;
	unsigned int (*input_filter_init)(void);
};
로그인 후 복사

이 구조입니다. CLI 라이프사이클에서 여기에 정의된 함수 포인터는 해당 기능을 구현하기 위해 호출됩니다.

또한 중요한 데이터 구조가 있습니다 —— sapi_globals, 해당 매크로는 SG(v)

struct _sapi_module_struct {
        char *name; // 名字,如cli、 fpm-fcgi等
        char *pretty_name; // 更易理解的名字,比如fpm-fcgi对应的为FPM/FastCGI
        int (*startup)(struct _sapi_module_struct *sapi_module);
        //模块启动时调用的函数
        int (*shutdown)(struct _sapi_module_struct *sapi_module);
        //模块结束时调用的函数
        int (*activate)(void); // 处理request时,激活需要调用的函数指针
        int (*deactivate)(void); // 处理完request时,使要调用的函数指针无效
        size_t (*ub_write)(const char *str, size_t str_length);
        // 这个函数指针用于输出数据
        void (*flush)(void *server_context); // 刷新缓存的函数指针
        zend_stat_t *(*get_stat)(void); // 判断对执行文件是否有执行权限
        char *(*getenv)(char *name, size_t name_len); // 获取环境变量的函数指针
        void (*sapi_error)(int type, const char *error_msg, ...)
            ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); // 错误处理函数指针
        int (*header_handler)(sapi_header_struct *sapi_header,
            sapi_header_op_enum op, sapi_headers_struct *sapi_headers);
            //调用header()时被调用的函数指针
        int (*send_headers)(sapi_headers_struct *sapi_headers);
        // 发送全部header的函数指针
        void (*send_header)(sapi_header_struct *sapi_header, void *server_context);
        // 发送某一个header的函数指针
        size_t (*read_post)(char *buffer, size_t count_bytes);
        // 获取HTTP POST中数据的函数指针
        char *(*read_cookies)(void);  // 获取cookie中数据的函数指针
        void (*register_server_variables)(zval *track_vars_array);
        // 从$_SERVER中获取变量的函数指针
        void (*log_message)(char *message, int syslog_type_int);
        // 输出错误信息函数指针
        double (*get_request_time)(void); // 获取请求时间的函数指针
        void (*terminate_process)(void);  // 调用exit退出时的函数指针
        char *php_ini_path_override;  // PHP的ini文件被复写的地址

        void (*default_post_reader)(void); //负责解析POST数据的函数指针
        void (*treat_data)(int arg, char *str, zval *destArray);
        // 对数据进行处理的函数指针
        char *executable_location; // 执行的地理位置
        int php_ini_ignore; // 是否不使用任何ini配置文件
        int php_ini_ignore_cwd; // 忽略当前路径的php.ini
        int (*get_fd)(int *fd); // 获取执行文件的fd的函数指针
        int (*force_http_10)(void); // 强制使用http 1.0版本的函数指针
        int (*get_target_uid)(uid_t *); // 获取执行程序的uid的函数指针
        int (*get_target_gid)(gid_t *); // 获取执行程序的gid的函数指针
        unsigned int (*input_filter)(int arg, char *var, char **val, size_t val_len,
            size_t *new_val_len);
        // 对输入进行过滤的函数指针。比如将输入参数填充到自动全局变量$_GET、$_POST、$_COOKIE中
        void (*ini_defaults)(HashTable *configuration_hash);
        // 默认的ini配置的函数指针,把ini配置信息存在HashTable中
        int phpinfo_as_text; // 是否输出phpinfo信息

        char *ini_entries; // 执行时附带的ini配置,可以使用php -d设置
        const zend_function_entry *additional_functions;
        // 每个SAPI模块特有的一些函数注册,比如cli的cli_get_process_title
        unsigned int (*input_filter_init)(void);
};
로그인 후 복사
입니다. 이 SG는 총 560바이트입니다. PHP 수명주기 동안 여러 번 사용되었습니다.

cgi_main.c를 살펴보면 전체 CGI 코드가 총 900줄이 넘습니다.

PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1) 일부 코드를 접어보세요. ZTS 관련(스레드 안전 등)

zend_signal_startupPHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1) 신호 처리 방법(Linux 신호에 대해서는 나중에 설명하겠습니다. 피트가 너무 큽니다.)

sapi_startup(&cgi_sapi_module)을 호출하고 초기화 작업을 수행합니다. sapi_model

typedef struct _sapi_globals_struct {
	void *server_context;
	sapi_request_info request_info;
	sapi_headers_struct sapi_headers;
	int64_t read_post_bytes;
	unsigned char post_read;
	unsigned char headers_sent;
	zend_stat_t global_stat;
	char *default_mimetype;
	char *default_charset;
	HashTable *rfc1867_uploaded_files;
	zend_long post_max_size;
	int options;
	zend_bool sapi_started;
	double global_request_time;
	HashTable known_post_content_types;
	zval callback_func;
	zend_fcall_info_cache fci_cache;
} sapi_globals_struct;
로그인 후 복사
PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1)

sapi_starup 메소드를 따르고

...
        sapi_startup(&cgi_sapi_module);
	fastcgi = fcgi_is_fastcgi();
	cgi_sapi_module.php_ini_path_override = NULL;
로그인 후 복사

조건이 당분간 무시되면 마지막으로 sapi_globals_ctor 함수

sapi_globals_ctor() 내부 로직으로 이동합니다
SAPI_API void sapi_startup(sapi_module_struct *sf)
{
	sf->ini_entries = NULL // ini_entries设置null
	sapi_module = *sf; // 把传进来的结构体赋值给sapi_module 
//  上面有关于sap_module的 定义   sapi_module_struct sapi_module;
//这里你可以理解为 初始化了sapi_module_struct 
 
#ifdef ZTS
	ts_allocate_fast_id(&sapi_globals_id, &sapi_globals_offset, sizeof(sapi_globals_struct), (ts_allocate_ctor) sapi_globals_ctor, (ts_allocate_dtor) sapi_globals_dtor);
# ifdef PHP_WIN32
	_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
# endif
#else
	sapi_globals_ctor(&sapi_globals);
#endif

#ifdef PHP_WIN32
	tsrm_win32_startup();
#endif

	reentrancy_startup();
}
로그인 후 복사

memset은 기본입니다. C언어로 메모리 초기화 방법
static void sapi_globals_ctor(sapi_globals_struct *sapi_globals)
{
	memset(sapi_globals, 0, sizeof(*sapi_globals));
	zend_hash_init(&sapi_globals->known_post_content_types, 8, NULL, _type_dtor, 1);
	php_setup_sapi_content_types();
}
로그인 후 복사

위에서는 560바이트의 새 메모리를 생성합니다.

zend_hash_init는 해시 테이블을 정의하는 것입니다(배열에 대해서는 나중에 설명하겠습니다...)
php_setup_sapi_content_types 내부 구현은

下面是 memset() 函数的声明。
void *memset(void *str, int c, size_t n)

参数
str -- 指向要填充的内存块。
c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
n -- 要被设置为该值的字符数。
返回值
该值返回一个指向存储区 str 的指针。
로그인 후 복사
The 마지막 reentrancy_startup은 스레드 안전이 켜진 경우에만 유효한 함수입니다

위의 전체 sapi_startup 메소드가 실행되었습니다

/* {{{ php_startup_sapi_content_types */
int php_startup_sapi_content_types(void)
{
	sapi_register_default_post_reader(php_default_post_reader);
	sapi_register_treat_data(php_default_treat_data);
	sapi_register_input_filter(php_default_input_filter, NULL);
	return SUCCESS;
}
로그인 후 복사

fcgi_is_fastcgi 메소드 실행을 시작하세요
Fastcgi.c 파일을 따라가서 가리키세요

	sapi_startup(&cgi_sapi_module);
	fastcgi = fcgi_is_fastcgi();
	cgi_sapi_module.php_ini_path_override = NULL;
로그인 후 복사

여기서 is_initialized는 기본 초기값 0

그래서 fcgi_init() 함수

int fcgi_is_fastcgi(void)
{
	if (!is_initialized) {
		return fcgi_init();
	} else {
		return is_fastcgi;
	}
}
로그인 후 복사

로 가서 fgi 프로토콜 관련 내용을 초기화했습니다

글이 너무 길어서 작성하지 못했습니다. 내일 보충하겠습니다. 먼저 gdb로 cli 모드를 분석하겠습니다PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1)

이 기사는 원저자 PHP Cui Xuefeng의 승인을 받아 PHP 중국어 웹사이트(원본 주소: https://zhuanlan.zhihu)에 게시되었습니다. .com/p/356037371

추천 학습: "

PHP 비디오 튜토리얼

"

위 내용은 PHP8의 기본 커널 소스 코드인 SAPI에 대한 심층 분석(1)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:zhihu.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿