ホームページ バックエンド開発 PHP8 PHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1)

PHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1)

Jun 10, 2021 pm 02:54 PM
php8

この記事では、PHP8 の基礎となるカーネル ソース コードを詳細に分析し、SAPI について学びます。一定の参考値があるので、困っている友達が参考になれば幸いです。

PHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1)

# おすすめ関連記事: 「

PHP8 の基礎となるカーネル ソース コードの分析 - 配列 (1)

in 以下の環境は 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://www.laruence.com/2008/08/12/180.html

PHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1) #SAPI (Server Application Programimg Interface、サーバー アプリケーション プログラミング インターフェイス) は、PHP の外部環境のプロキシに相当します。 PHPは端末上で適用することもできますが、端末上で適用するSAPIをCLI SAPI、Webサーバー上で適用するSAPIをCGI SAPIと呼びます。

これは、前後を接続する中間層または接着剤に相当します。

sapi のコア定義とマクロ ファイルは、sapi.h

# にあります。

## cgi_main.cPHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1)

//* 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
};
ログイン後にコピー

に重要な構造があります_sapi_module_structを「継承」しています

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);
};
ログイン後にコピー

#コメント後は次のようになります

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);
};
ログイン後にコピー

この構造は非常に重要です。CLI ライフサイクルでは、その中で定義された関数ポインターがそれぞれの関数を実装するために呼び出されます。重要なデータ構造 (sapi_globals) もあります。対応するマクロは SG(v)

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;
ログイン後にコピー

この SG は合計 560 バイトで、PHP ライフ サイクルを通じて何度も使用されています

PHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1) cgi_main.c を振り返ると、cgi コード全体が合計 900 行を超えています。

ZTS (スレッド セーフティなど) に関連するコードをいくつか折り畳んでください

PHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1)

zend_signal_startup PHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1) 信号処理メソッド; (Linux 信号については後で説明します。ピットが大きすぎます)

sapi_startup(&cgi_sapi_module) を呼び出していくつかの処理を実行します。 sapi_model での初期化作業

...
        sapi_startup(&cgi_sapi_module);
	fastcgi = fcgi_is_fastcgi();
	cgi_sapi_module.php_ini_path_override = NULL;
ログイン後にコピー

sapi_starup メソッドに従います

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();
}
ログイン後にコピー

今のところ if 条件を無視して、最後に sapi_globals_ctor 関数に進みます

sapi_globals_ctor() 内部ロジック

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();
}
ログイン後にコピー

memset は C 言語でメモリを初期化するネイティブ メソッドです

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

参数
str -- 指向要填充的内存块。
c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
n -- 要被设置为该值的字符数。
返回值
该值返回一个指向存储区 str 的指针。
ログイン後にコピー

上記により、 560 バイトの新しいメモリ

zend_hash_init はハッシュテーブル (配列については後で説明します...) を定義するためのものです。

php_setup_sapi_content_types の内部実装は

/* {{{ 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;
}
ログイン後にコピー

です。

最後の reentrancy_startup は、スレッド セーフティがオンになっている場合にのみ有効な関数です

上記の sapi_startup メソッド全体が実行されました

	sapi_startup(&cgi_sapi_module);
	fastcgi = fcgi_is_fastcgi();
	cgi_sapi_module.php_ini_path_override = NULL;
ログイン後にコピー

fcgi_is_fastcgi の実行を開始しますmethod

フォローアップして、fastcgi.c ファイルを指定します。 file

int fcgi_is_fastcgi(void)
{
	if (!is_initialized) {
		return fcgi_init();
	} else {
		return is_fastcgi;
	}
}
ログイン後にコピー

is_initialized のデフォルトの初期値は 0

それでは、fcgi_init() 関数に進みます

PHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1)

int fcgi_init(void)
{
	if (!is_initialized) {
#ifndef _WIN32
		sa_t sa;
		socklen_t len = sizeof(sa);
#endif
		zend_hash_init(&fcgi_mgmt_vars, 8, NULL, fcgi_free_mgmt_var_cb, 1);
		fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS")-1, "0", sizeof("0")-1);

		is_initialized = 1;
#ifdef _WIN32
# if 0
		/* TODO: Support for TCP sockets */
		WSADATA wsaData;

		if (WSAStartup(MAKEWORD(2,0), &wsaData)) {
			fprintf(stderr, "Error starting Windows Sockets.  Error: %d", WSAGetLastError());
			return 0;
		}
# endif
		if ((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
		    (GetStdHandle(STD_ERROR_HANDLE)  == INVALID_HANDLE_VALUE) &&
		    (GetStdHandle(STD_INPUT_HANDLE)  != INVALID_HANDLE_VALUE)) {
			char *str;
			DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT;
			HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE);

			SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL);

			str = getenv("_FCGI_SHUTDOWN_EVENT_");
			if (str != NULL) {
				zend_long ev;
				HANDLE shutdown_event;

				ZEND_ATOL(ev, str);
				shutdown_event = (HANDLE) ev;
				if (!CreateThread(NULL, 0, fcgi_shutdown_thread,
				                  shutdown_event, 0, NULL)) {
					return -1;
				}
			}
			str = getenv("_FCGI_MUTEX_");
			if (str != NULL) {
				zend_long mt;
				ZEND_ATOL(mt, str);
				fcgi_accept_mutex = (HANDLE) mt;
			}
			return is_fastcgi = 1;
		} else {
			return is_fastcgi = 0;
		}
#else
		errno = 0;
		if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) {
			fcgi_setup_signals();
			return is_fastcgi = 1;
		} else {
			return is_fastcgi = 0;
		}
#endif
	}
	return is_fastcgi;
}
ログイン後にコピー

fgi プロトコル関連のコンテンツを初期化します

この記事は長すぎて書けません。明日。まず gdb で cli モードを分析します。

この記事は、原著者 PHP Cui Xuefeng によって承認され、PHP 中国語 Web サイトに公開されました。元のアドレスは: https://zhuanlan.zhihu .com/p/356037371

推奨学習: 「

PHP ビデオ チュートリアル

以上がPHP8 の基礎となるカーネル ソース コードである SAPI の詳細な分析 (1)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

php5とphp8の違いは何ですか php5とphp8の違いは何ですか Sep 25, 2023 pm 01:34 PM

php5 と php8 の違いは、パフォーマンス、言語構造、型システム、エラー処理、非同期プログラミング、標準ライブラリ関数、セキュリティの点です。詳細な紹介: 1. パフォーマンスの向上. PHP5 と比較して、PHP8 はパフォーマンスが大幅に向上しています. PHP8 では、高頻度の実行コードをコンパイルおよび最適化できる JIT コンパイラーが導入されており、それによって実行速度が向上しています; 2. 言語構造の改善、 PHP8 では、いくつかの新しい言語構造と関数が導入されており、PHP8 では名前付きパラメータがサポートされており、開発者はパラメータの順序などの代わりにパラメータ名を渡すことができます。

mysql拡張機能をphp8に追加する方法 mysql拡張機能をphp8に追加する方法 Oct 07, 2023 pm 03:31 PM

mysql 拡張機能を php8 に追加する手順は次のとおりです: 1. MySQL クライアント ライブラリをインストールします; 2. PHP 8 の開発ツールをインストールします; 3. MySQL 拡張機能のソース コードをダウンロードします; 4. MySQL 拡張機能をコンパイルしてインストールします; 5. MySQL 拡張機能; 6. Web サーバーのみを再起動します。

apache2.4+php8.0のインストールと設定方法を図解で詳しく解説 apache2.4+php8.0のインストールと設定方法を図解で詳しく解説 Dec 06, 2022 pm 04:53 PM

この記事では、apache2.4のインストール方法とphp8.0の設定方法を画像付きで詳しく紹介します。apache2.4+php8.0のインストールと設定方法を見てみましょう~

php8でデータベースに接続する方法 php8でデータベースに接続する方法 Nov 16, 2023 pm 02:41 PM

PHP8 は、mysqli と PDO を使用してデータベースに接続できます。詳細な紹介: 1. mysqli を使用して、データベース サーバー名、ユーザー名、パスワード、および接続するデータベース名を渡してデータベースに接続します。次に、`connect_error` 属性を使用して接続が成功したかどうかを確認し、接続が失敗した場合はエラー メッセージを出力します。最後に、`close()` メソッドを呼び出して接続を閉じます; 2. PDO を使用してデータベースに接続し、データベース サーバー名、パスワード、データベース名などを渡して接続します。

php8のデータ型を変換する方法 php8のデータ型を変換する方法 Nov 16, 2023 pm 02:51 PM

php8 データ型のメソッドには、文字列から整数への変換、整数から文字列への変換、文字列から浮動小数点数への変換、浮動小数点数から文字列への変換、配列から文字列への変換、文字列から配列への変換、およびブール値から整数への変換が含まれます。 . 整数からブール値への変換、変数の型の決定と変換。詳細な紹介: 1. 文字列から整数への変換には、intval() 関数と (int) 強制型変換が含まれます; 2. 整数から文字列への変換には、strval() 関数と (string) 強制型変換が含まれます; 3. 変換文字列を浮動小数点ポイントに変換するなどです。

PHP 8 の JIT の詳細 PHP 8 の JIT の詳細 Apr 25, 2022 pm 08:46 PM

この記事では、PHP 8 の JIT について説明し、JIT が解釈プロセスにどのように参加するかについて説明します。

php8 ではどのようなパフォーマンスの向上がありますか? php8 ではどのようなパフォーマンスの向上がありますか? Dec 21, 2023 pm 02:44 PM

php8 のパフォーマンス向上には、1. JIT コンパイラの導入、2. 関数呼び出しの最適化、3. ガベージ コレクション メカニズムの改善、4. 型システムの改善、5. 新しい言語機能、6. 文字列処理の最適化、7.配列処理の改善、8. 新しいメモリ管理メカニズムの導入、9. コード生成の最適化。詳細な紹介: 1. JIT コンパイラーの導入 PHP8 では、より効率的に実行するために PHP コードをマシンコードに変換できる動的コンパイル技術である JIT コンパイラーが導入されています; 2. 関数呼び出しの最適化など。

php8にはどのような機能があるのでしょうか? php8にはどのような機能があるのでしょうか? Dec 21, 2023 pm 02:54 PM

php8 の機能: 1. JIT コンパイラ; 2. 名前付きパラメータ; 3. 共用体型; 4. アノテーション; 5. コンストラクタ属性の強化; 6. 一致式; 7. nullsafe 演算子; 8. 型システムの改善; 9 、エラー処理の改善; 10. 一貫性の向上。詳細な紹介: 1. JIT コンパイラ、PHP8 は、PHP コードをネイティブ マシン コードにコンパイルするジャストインタイム コンパイル テクノロジを導入し、それによってプログラムの実行速度を向上させます; 2. 名前付きパラメータ、PHP8 は名前付きパラメータをサポートし、関数呼び出し時のパラメータ名など

See all articles