Maison développement back-end PHP8 Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

Jun 10, 2021 pm 02:54 PM
php8

Cet article vous donnera une analyse approfondie du code source du noyau sous-jacent de PHP8 et en apprendra davantage sur SAPI. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il sera utile à tout le monde.

Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

Articles connexes recommandés : "Analyse du code source du noyau sous-jacent PHP8 - tableau (1) "

dans L'environnement suivant est construit sous 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]#
Copier après la connexion

Présentation d'une image de Brother Bird

Compréhension approfondie des Zend SAPI (Zend SAPI Internals)

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

Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

SAPI (Server Application Programming Interface, interface de programmation d'application serveur) est équivalent au proxy de l'environnement externe PHP. PHP peut être appliqué sur le terminal ou sur le serveur Web. Le SAPI appliqué sur le terminal est appelé CLI SAPI, et le SAPI appliqué sur le serveur Web est appelé CGI SAPI.

C'est l'équivalent d'une couche intermédiaire ou d'une colle qui relie la précédente et la suivante

La définition de base et les fichiers macro de sapi sont dans sapi.h

Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

Il y a une structure importante

//* 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
};
Copier après la connexion

dans cgi_main.c qui "hérite" de la structure _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);
};
Copier après la connexion

et est commentée comme suit

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);
};
Copier après la connexion

La structure est très critique. Dans le cycle de vie de la CLI, les pointeurs de fonction qui y sont définis seront appelés pour implémenter leurs fonctions respectives

Il existe également une structure de données importante - sapi_globals, et sa la macro correspondante est SG(v)

Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

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;
Copier après la connexion

Ce SG a un total de 560 octets et a été utilisé plusieurs fois tout au long du cycle de vie de PHP

Rétrospective sur cgi_main.c, l'intégralité du code cgi compte un total de plus de 900 lignes

Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

Pliez du code impliquant ZTS (sécurité des threads et autres)

Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

zend_signal_startup Méthode de traitement du signal ; (Les signaux Linux seront discutés plus tard. La fosse est trop grande)

Appelez sapi_startup(&cgi_sapi_module) pour effectuer un travail d'initialisation sur sapi_model

...
        sapi_startup(&cgi_sapi_module);
	fastcgi = fcgi_is_fastcgi();
	cgi_sapi_module.php_ini_path_override = NULL;
Copier après la connexion

Suivez la méthode 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();
}
Copier après la connexion

Ignorez la condition if pour l'instant et enfin allez dans la fonction sapi_globals_ctor

logique interne 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();
}
Copier après la connexion

memset est la méthode native d'initialisation de la mémoire en langage C

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

参数
str -- 指向要填充的内存块。
c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
n -- 要被设置为该值的字符数。
返回值
该值返回一个指向存储区 str 的指针。
Copier après la connexion

Ce qui précède crée une nouvelle mémoire de 560 octets

zend_hash_init définit une table de hachage (tableau On en reparle plus tard. )

L'implémentation interne de php_setup_sapi_content_types est

/* {{{ 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;
}
Copier après la connexion

Le dernier reentrancy_startup est une fonction qui n'est valide que lorsque la sécurité des threads est activée

L'intégralité de la méthode sapi_startup ci-dessus a été exécutée

	sapi_startup(&cgi_sapi_module);
	fastcgi = fcgi_is_fastcgi();
	cgi_sapi_module.php_ini_path_override = NULL;
Copier après la connexion

Commencer à exécuter la méthode fcgi_is_fastcgi

Suivez et pointez sur le fichier fastcgi.c file

int fcgi_is_fastcgi(void)
{
	if (!is_initialized) {
		return fcgi_init();
	} else {
		return is_fastcgi;
	}
}
Copier après la connexion

La valeur initiale par défaut de is_initialized est 0

Analyse approfondie de SAPI, le code source du noyau sous-jacent de PHP8 (1)

Alors allez sur la fonction fcgi_init()

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;
}
Copier après la connexion

Initialiser le contenu lié au protocole fgi

Cet article est trop long à écrire, je le rattraperai demain. Analysons le mode cli. avec gdb d'abord

Cet article a été approuvé par l'auteur original PHP Cui Xuefeng et publié sur le site Web PHP chinois L'adresse originale est : https://zhuanlan.zhihu.com/p/356037371<.>

Apprentissage recommandé : "

Tutoriel vidéo PHP"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Quelle est la différence entre php5 et php8 Quelle est la différence entre php5 et php8 Sep 25, 2023 pm 01:34 PM

Les différences entre php5 et php8 concernent les performances, la structure du langage, le système de types, la gestion des erreurs, la programmation asynchrone, les fonctions de bibliothèque standard et la sécurité. Introduction détaillée : 1. Amélioration des performances. Par rapport à PHP5, PHP8 présente une énorme amélioration des performances. PHP8 introduit un compilateur JIT, qui peut compiler et optimiser certains codes d'exécution à haute fréquence, améliorant ainsi la vitesse d'exécution. PHP8 introduit de nouvelles structures et fonctions de langage. PHP8 prend en charge les paramètres nommés, permettant aux développeurs de transmettre les noms des paramètres au lieu de l'ordre des paramètres, etc.

Comment ajouter une extension MySQL à PHP8 Comment ajouter une extension MySQL à PHP8 Oct 07, 2023 pm 03:31 PM

Les étapes pour ajouter l'extension MySQL à PHP8 sont : 1. Installez la bibliothèque client MySQL ; 2. Installez les outils de développement pour PHP 8 ; 3. Téléchargez le code source de l'extension MySQL ; 4. Compilez et installez l'extension MySQL ; Extension MySQL ; 6. Redémarrez Juste un serveur Web.

Explication graphique détaillée de la méthode d'installation et de configuration d'Apache2.4+php8.0 Explication graphique détaillée de la méthode d'installation et de configuration d'Apache2.4+php8.0 Dec 06, 2022 pm 04:53 PM

Cet article vous présentera comment installer Apache2.4 et comment configurer php8.0. L'article est accompagné d'images et d'étapes détaillées. Voyons comment installer et configurer Apache2.4+php8.0~.

Comment se connecter à la base de données en php8 Comment se connecter à la base de données en php8 Nov 16, 2023 pm 02:41 PM

PHP8 peut utiliser mysqli et PDO pour se connecter à la base de données. Introduction détaillée : 1. Utilisez mysqli pour vous connecter à la base de données en transmettant le nom du serveur de base de données, le nom d'utilisateur, le mot de passe et le nom de la base de données pour vous connecter. Ensuite, utilisez l'attribut `connect_error` pour vérifier si la connexion réussit et affichez un message d'erreur si la connexion échoue. Enfin, fermez la connexion en appelant la méthode `close()` ; 2. Utilisez PDO pour vous connecter à la base de données, et connectez-vous en passant le nom du serveur de base de données, le mot de passe et le nom de la base de données, etc.

Comment convertir les types de données php8 Comment convertir les types de données php8 Nov 16, 2023 pm 02:51 PM

Les méthodes du type de données php8 incluent la conversion d'une chaîne en entier, la conversion d'un entier en chaîne, la conversion d'une chaîne en nombre à virgule flottante, la conversion d'un nombre à virgule flottante en chaîne, la conversion d'un tableau en chaîne, la conversion d'une chaîne en tableau, la conversion d'une valeur booléenne en entier, la conversion d'un entier en Détermination et conversion de valeurs booléennes et de types de variables. Introduction détaillée : 1. La conversion d'une chaîne en un entier inclut la fonction intval() et la conversion de type forcée (int) ; 2. La conversion d'un entier en chaîne inclut la fonction strval() et la conversion de type forcée (string) ; une chaîne à un flotteur Points et ainsi de suite.

Un examen approfondi de JIT dans PHP 8 Un examen approfondi de JIT dans PHP 8 Apr 25, 2022 pm 08:46 PM

Cet article vous guidera à travers le JIT dans PHP 8 et expliquera comment JIT participe au processus d'interprétation. J'espère qu'il sera utile à tout le monde !

Quelles améliorations de performances apporte php8 ? Quelles améliorations de performances apporte php8 ? Dec 21, 2023 pm 02:44 PM

Les performances améliorées de php8 incluent : 1. Introduction du compilateur JIT ; 2. Optimisation des appels de fonctions ; 3. Amélioration du mécanisme de récupération de place ; 4. Amélioration du système de types ; 6. Optimisation du traitement des chaînes ; . Améliorer le traitement des tableaux ; 8. Introduire un nouveau mécanisme de gestion de la mémoire ; 9. Optimiser la génération de code. Introduction détaillée : 1. L'introduction du compilateur JIT PHP8 introduit le compilateur JIT, qui est une technologie de compilation dynamique qui peut convertir le code PHP en code machine pour une exécution plus efficace ;

Quelles sont les fonctionnalités de php8 ? Quelles sont les fonctionnalités de php8 ? Dec 21, 2023 pm 02:54 PM

Caractéristiques de php8 : 1. Compilateur JIT ; 2. Paramètres nommés ; 3. Types d'union ; 4. Annotations ; Amélioration des attributs du constructeur ; 7. Opérateur nullsafe ; ; 10. Améliorations de la cohérence. Introduction détaillée : 1. Compilateur JIT, PHP8 introduit la technologie de compilation juste à temps, qui compile le code PHP en code machine natif, améliorant ainsi la vitesse d'exécution du programme 2. Paramètres nommés, PHP8 prend en charge les paramètres nommés, permettant l'utilisation de noms de paramètres lors de l’appel de fonctions, etc.

See all articles