Translation [PHP extension development and Embedded] Chapter 1th-php life cycle

Source: Internet
Author: User
Tags sapi zts zend
The life cycle of PHP

In a common webserver environment, you cannot start the PHP interpreter directly; This is typically initiated by Apache or other webserver, which loads PHP processing scripts that need to be processed (requested. PHP documentation).

Everything starts with the SAPI.

Although it may look different, the CLI behaves in the same way as the web. Typing the PHP command at the command line launches command line SAPI, which is actually like a mini-version webserver designed for service order requests. When the script is finished, the mini-Php-webserver terminates and returns control to the shell.

Start and stop

The start-up and termination processes are divided into two separate start-up stages and two separate termination phases. A cycle is used to initialize the structure and values required for the overall execution of the PHP interpreter, and they persist over the SAPI life cycle. The other only serves single-page requests, with a short life cycle.

Initialization starts before all requests occur, PHP calls each extended Minit (module initialization) method. Here, the extension may define constants, define classes, register resources, stream, filter processors, and so on all the resources that will be used by the requested script. All of these have a feature that they are designed to exist across all requests, or to be called "persistent."

The common Minit methods are as follows:

/* Initialize the Myextension module * This will occur immediately after SAPI startup */Php_minit_function (myextension) {/* Global: Chapter 12th */#ifdef ZTS Ts_al locate_id (&myextension_globals_id, sizeof (Php_myextension_globals), (ts_allocate_ctor) Myextension_g  Lobals_ctor, (Ts_allocate_dtor) myextension_globals_dtor);  #else myextension_globals_ctor (&myextension_globals tsrmls_cc);          #endif/* Register_ini_entries () points to a global structure, we will learn */register_ini_entries () in the 13th chapter "INI Settings"; /* equivalent to define (' myext_meaning ', 42); */Register_long_constant ("myext_meaning", Const_cs |      Const_persistent); /* equivalent to define (' Myext_foo ', ' Bar '); */Register_string_constant ("Myext_foo", "Bar", Const_cs |          Const_persistent); /* Resources: Chapter 9th */Le_myresource = ZEND_REGISTER_LIST_DESTRUCTORS_EX (Php_myext_myresource_dtor, NULL      , "My Resource Type", module_number);        Le_myresource_persist = ZEND_REGISTER_LIST_DESTRUCTORS_EX (              NULL, Php_myext_myresource_dtor, "My Resource Type", module_number); /* Stream Filter: 16th Chapter */if (FAILURE = = Php_stream_filter_register_factory ("Myfilter", &php_myextensi      On_filter_factory tsrmls_cc) {return FAILURE; }/* Flow wrapper: 15th Chapter */if (FAILURE = = Php_register_url_stream_wrapper ("Myproto", &php_m      Yextension_stream_wrapper tsrmls_cc) {return FAILURE; }/* Auto global variable: the 12th Chapter */#ifdef zend_engine_2 if (Zend_register_auto_global ("_myextension", sizeof ("_myextensio      N ")-1, NULL tsrmls_cc) = = FAILURE) {return FAILURE;                                                       } zend_auto_global_disable_jit ("_myextension", sizeof ("_myextension")-1  TSRMLS_CC);                                         #else if (Zend_register_auto_global ("_myextension", sizeof ("_myextension")-1              TSRMLS_CC) = = FAILURE) {return FAILURE;  } #endif return SUCCESS; }

When a request arrives, PHP installs an operating environment that contains the symbol table (variable storage) and synchronizes the configuration values for each directory. PHP then iterates through all the extensions, this time invoking the Rinit (request initialization) method for each extension. Here, the extension may recharge the global variable to the default value, preset the variable to the script's symbol table, or perform other tasks such as logging the page request log to the file. Rinit to all script requests just like the auto_prepend_file directive.

The Rinit method is spelled as follows:

/* Execute before each page request starts  *  /php_rinit_function (myextension)  {      zval *myext_autoglobal;          /* Initialize the automatic global variable defined in the Minit function as an empty array. This is equivalent to $_myextension = Array (); *      /Alloc_init_zval (Myext_autoglobal);      Array_init (Myext_autoglobal);      Zend_hash_add (&eg (symbol_table), "_myextension", sizeof ("_myextension")-1,                                  (void**) &myext_autoglobal , sizeof (zval*), NULL);          return SUCCESS;  }

After a request finishes processing (reaching the end of the script file or invoking the die ()/exit () statement), PHP begins the cleanup process by invoking the Rshutdown (request termination) of each extension. Just as rinit corresponds to Auto_prepend_file, rshutdown can be analogous to auto_append_file directives. The most important difference between Rshutdown and Auto_append_file is that, in any case, the Rshutdown is always executed, and the Die ()/exit () Call of the user space script skips all auto_append_file.

The last thing that needs to be done before the symbol table and other resources are released is the old Rshutdown. After all the Rshutdown methods are complete, all the variables in the symbol table are immediately unset (),

During this time, all destructors for non-persisted resources and objects are called to gracefully release resources.

/* Call *  /php_rshutdown_function (myextension)  {      zval **myext_autoglobal after          each page request ends; if (Zend_hash_find (&eg (symbol_table), "_myextension", sizeof ("_myextension"),                                           (void**) &myext_ Autoglobal) = = SUCCESS) {          /* do some meaningful processing of the value of the $_myextension array *          /php_myextension_handle_values (Myext_autoglobal TSRMLS_CC);      }      return SUCCESS;  }

Finally, when all the requests are fulfilled (processing), the webserver or other SAPI start to terminate, and the PHP loop executes the Mshutdown (module termination) method for each extension. This is the chance for the Minit cycle to extend the last time the processor is unloaded and the memory of the persisted allocation is freed.

/* This module is being unloaded, constants and functions will be unloaded automatically, persistent resources, classes, stream processors must be manually unloaded. *  /Php_mshutdown_function (myextension)  {      unregister_ini_entries ();      Php_unregister_url_stream_wrapper ("Myproto" tsrmls_cc);      Php_stream_filter_unregister_factory ("Myfilter" tsrmls_cc);      return SUCCESS;  }

Life cycle

Each PHP instance, whether launched from the Init script or from the command line, is followed by the initialization/termination events for a series of requests/modules described in the previous section, as well as the execution of the script itself. How many times will each start and end phase be executed? What frequency does it take? are dependent on the SAPI used. The following is a discussion of the 4 most common sapi:cli/cgi, multi-process modules, multithreaded modules, embedded.

CLI life cycle

The CLI (and CGI) SAPI is quite special in its single request life cycle, because there is only one request for the entire PHP lifecycle at this time. However, the perfectly formed, the above-mentioned stages will still be fully implemented. is to invoke the PHP interpreter from the command line to process the processing of the test.php script:

The most common use of PHP is to build PHP into Apache 1 or the Apache 2 APXS module using Pre-fork mpm, which embeds PHP into webserver. There are other webserver that belong to this category, which are collectively referred to as "multi-process modules" later in this book.

They are called multi-process modules because when Apache starts, it immediately fork out a number of sub-processes, each with its own independent process space, independent of each other. In a child process, the life cycle of the PHP instance is the same as shown. The only change here is that multiple requests are caught in a single minit/mshutdown pair:

This mode does not allow any child processes to know the data owned by other child processes, but it allows the child process to die and be replaced without affecting the stability of the other child processes. The warrior has multiple processes in an Apache instance and their calls to Minit, Rinit, Rshutdown, Mshutdown methods.

Multi-threaded life cycle

With development, PHP is gradually being used by some webserver in multiple threads, such as the ISAPI interface of IIS, Apapche 2 worker mpm. There is always only one process running in multithreaded webserver, but there are multiple threads executing in the process space at the same time. This reduces some of the load, including avoiding repeated calls to Minit/mshutdown, where real global data is only allocated and initialized once, potentially opening multiple requests for information sharing. Demonstrates the status of the process when running PHP on a multithreaded webserver such as Apache 2:

Embedded life cycle

Looking back, the embedded SAPI is just another implementation of SAPI, and it follows the same rules as the CLI, APXS, and ISAPI interfaces, so it's easy to guess that the life cycle of the request follows the same basic path: module initialization = Request initialization +/request termination + = module terminated. In fact, the embedded SAPI follows these steps as well as its kindred.

What makes the embedded SAPI special is that it can be infiltrated into multiple script fragments as part of an entire request. In most cases, control is passed back and forth between PHP and the calling application.

While an embedded request may consist of one or more code elements, the embedded application is affected by the same request isolation as webserver. In order to handle two or more parallel embedded environments, your app either goes fork like Apache 1, or the old one is threaded as Apache 2. Trying to process two separate request environments in a single, non-threaded process space will produce unpredictable results, which is certainly something you don't expect.

Zend Thread Safety

When PHP was in early childhood, it ran as a single-process CGI, with no thread-safe concept because there was no process space longer than a single request. Internal variables can be defined in the global scope, accessed, modified, as long as the initialization without problems will have no serious consequences. Any resources that are not properly cleaned will be released when the CGI process terminates.

Later, PHP embedded a multi-process webserver, such as Apache. The given internal variable can still be defined globally and can be secured by the proper initialization at the start of each request, and the appropriate cleanup to be done at the time of termination, because there is only one request in a process space. This time, increase the memory management of each request to place a loss of control over the growth of resource leaks.

Once the single-process multithreading webserver appears, a new method for global data processing is needed. And finally, as a new layer of TSRM (thread-safe resource management)

Thread safety vs. waste thread safety Definitions

In a simple non-threaded application, you might like to define global variables and place them at the top of your source code. The compiler allocates memory blocks to the data segment of your program to save information.

In a multithreaded application, each thread needs its own data element, and each thread needs to allocate a separate block of memory. A given thread needs to be able to properly access its own block of memory when it needs to access its own data.

Thread-Safe Data pools

In an extended minit phase, the extension can call ts_allocate_id () to tell the TSRM layer one or more times how much data space it needs, TSRM receive a notification, increase the total run data space by the requested number of bytes, and return a new unique identity. Marks the data segment portion of the thread data pool.

typedef struct {      int sampleint;      char *samplestring;  } Php_sample_globals;  int sample_globals_id;  Php_minit_function (sample)  {      ts_allocate_id (&sample_globals_id,          sizeof (php_sample_globals),          (Ts_allocate_ctor) Php_sample_globals_ctor,          (ts_allocate_dtor) php_sample_globals_dtor);      return SUCCESS;  }

When a request requires access to a data segment, the extension requests the resource pool of the current thread from the TSRM layer to get the offset from the resource ID returned by ts_allocate_id ().

In other words, in the code flow, you might encounter Sample_g (sampleint) = 5 in the Minit statement that you said earlier, such a statement. In the context of thread security, this statement is expanded by some macros as follows:

(((php_sample_globals*) (* (void * *) Tsrm_ls) [sample_globals_id-1])->sampleint = 5;

If you do not understand the above conversion is not depressed, it has been well encapsulated in the PHPAPI, so many developers do not need to know how it works.

When not in the thread environment

Because accessing global resources in the thread-safe construction of PHP involves finding the corresponding offsets for the threads data pool, this is some additional load, and the result is that it is slower than the corresponding non-threading approach (fetching data directly from the actual global variable address that was computed at the compile time).

Consider the above example, this time in a non-threaded build:

typedef struct {      int sampleint;      char *samplestring;  } Php_sample_globals;  Php_sample_globals sample_globals;  Php_minit_function (sample)  {      php_sample_globals_ctor (&sample_globals tsrmls_cc);      return SUCCESS;  }

The first thing to notice is that there is no definition of an int to refer to the global structure definition, but simply a struct is defined in the global space of the process. That is to say Sample_g (sampleint) = 5; unfold is sample_globals.sampleint = 5; Simple, fast and efficient.

Non-threading constructs also have the advantage of process isolation, so that when a given request encounters a completely unexpected situation, it does not affect other processes, even if a segment error does not cause the entire webserver to be paralyzed. In fact, Apache's Maxrequestsperchild instruction is designed to enhance this feature, and it often purposefully kill the child process and generate new sub-processes to avoid some problems that may be "accumulating" due to the process's long running time (such as memory leaks).

Accessing global variables

When you create an extension, you do not know whether its final run environment is thread-safe. Fortunately, the standard you want to use contains ZTS preprocessing tokens that already contain conditional definitions in the file collection. This value is automatically defined when PHP is built in a thread-safe manner because SAPI is required or installed through the ENABLE-MAINTAINER-ZTS option, and it can be tested with a set of #ifdef zts such a set of instructions to test its value.

As you can see earlier, only the thread-safe pool exists when PHP is compiled in a thread-safe manner, and only when the thread-safe pool exists does it really allocate space in the online security pool. This is why the previous example wrapped in the Zts check, non-threaded for use by non-threaded builds.

In the Php_minit_function (myextension) example earlier in this chapter, you can see that #ifdef ZTS is used as a conditional invocation of the correct global initial code. For the ZTS mode it uses ts_allocate_id () to eject the myextension_globals_id variable, instead of the ZTS mode simply calling the Myextension_globals initialization method directly. These two variables have been declared in your extension source file using the Zend macro: declare_module_globals (myextension) declaration, which automatically handles the test of ZTS and relies on the built ZTS mode to choose the correct way to declare.

When accessing these global variables, you need to use the custom macro Sample_g () given above. In the 12th chapter, you will learn how to design the macro so that it can rely on Zts mode to expand automatically.

Consider threading even if you don't need threads

Normal PHP builds are off-thread-safe by default, and only when the SAPI that are built explicitly require thread-safety or thread-safety to be explicitly opened in the./configure phase before they are built in a thread-safe manner.

Given the speed problem of global lookups and the drawbacks of process isolation, you may wonder why people do not need to open it intentionally. This is because, in most cases, the developers of extensions and SAPI think that you are the operator of a thread-safe switch, which can be a great way to ensure that the new code can function properly in all environments.

When thread safety is enabled, a special pointer named Tsrm_ls is added to a number of intrinsic function prototypes. This pointer allows PHP to differentiate data from different threads. Recall that it was used in the Sample_g () macro function in Zts mode earlier in this chapter. Without it, the executing function does not know which thread's symbol table to find and set, which script it should execute, and the engine is completely unable to track its internal registers. This pointer retains all page requests that are processed by the thread.

This optional pointer parameter is included in the prototype by the following set of definitions. When ZTS is disabled, these definitions are expanded to null, and when ZTS is turned on, they expand as follows:

#define Tsrmls_d     void ***tsrm_ls  #define TSRMLS_DC     , void ***tsrm_ls  #define Tsrmls_c     Tsrm_ls  #define TSRMLS_CC     , Tsrm_ls

Non-ZTS builds see two parameters for the following code: int, char *. Under ZTS Construction, the prototype contains three parameters: int, char *, void * *. When your program calls this function, you only need to pass the third argument when ZTS is enabled. The second line of the following code shows the expansion of the macro:

int php_myext_action (int action_id, char *message tsrmls_dc);  Php_myext_action ("The Meaning of Life" tsrmls_cc);

By including this special variable in the function call, php_myext_action can use the value of Tsrm_ls and the Myext_g () macro function to access its line thread have global data. On non-ZTS builds, Tsrm_ls will not be available, but this is OK because the Myext_g () macro function and other similar macros do not use it at this time.

Now consider that you are working on a new extension and have the following function, which can work properly on your local build using the CLI SAPI, and will work even if you are compiling with Apache 1 apxs SAPI:

static int Php_myext_isset (char *varname, int varname_len)  {      zval **dummy;          if (Zend_hash_find (EG (active_symbol_table),          varname, Varname_len + 1,          (void**) &dummy) = = SUCCESS) {          / * Variable exists */          return 1;      } else {/          * Undefined Variable */          return 0;  }}

Everything seems to work fine, you pack this extension and send it to others to build and run on the production server. What makes you discouraged is that the other party reports that the extension compilation failed.

In fact, they used the Apache 2.0 threading pattern, so their PHP build enabled Zts. When the compile time encounters the eg () macro function that you are using, it tries to find Tsrm_ls in the local space, because you do not define it and do not pass it in your function.

Fixing the problem is very simple, just add tsrmls_dc to the definition of Php_myext_isset () and add tsrmls_cc to the place where each row calls it. Unfortunately, now the other side has a little distrust of your extension quality, which will delay your demo cycle. The sooner the problem is resolved, the better.

Now we have the ENABLE-MAINTAINER-ZTS directive. By adding this directive to the./configure to build PHP, your build will automatically contain ZTS, even if your current SAPI (such as the CLI) does not need it. By opening this switch, you can avoid these common errors that should not occur.

Note: In PHP4, the equivalent name of the enable-maintainer-zts tag is enable-experimental-zts; Be sure to use the correct tag for your PHP version.

Recover lost Tsrm_ls

Sometimes we need to use the Tsrm_ls pointer in a function, but we can't pass it. Typically this is because your extension is an interface to a library that uses callbacks, and it does not provide a place to return abstract pointers. Consider the following code snippet:

void Php_myext_event_callback (int eventtype, char *message)  {      zval *event;          /* $event = Array (' event ' = = $eventtype,                     ' message ' = + $message) *      /Make_std_zval (event);      Array_init (event);      Add_assoc_long (Event, "type", EventType);      Add_assoc_string (event, "message", message, 1);          /* $eventlog [] = $event; *      /Add_next_index_zval (Ext_g (EventLog), event);  }  Php_function (Myext_startloop)  {      /* the EVENTLIB_LOOPME () FUNCTION,      * Exported by an external library,< c15/>* waits for a event to happen,      * then dispatches it to the      * callback handler specified.      *      /EVENTLIB_LOOPME (php_myext_event_callback);  }

While you may not fully understand this code, you should note that the Ext_g () macro function is used in the callback function, and we know that it requires a Tsrm_ls pointer when the thread is securely built. Modifying a function prototype is not a good idea and should not be done because the external library does not know the thread safety model of PHP. So how do you make Tsrm_ls available in this situation?

The solution is the previously mentioned Zend macro function named Tsrmls_fetch (). Place it at the top of the code fragment, which will perform a lookup that gives the current thread context and define a local copy of the Tsrm_ls pointer.

This macro can be used anywhere and without passing Tsrm_ls through a function call, although this may seem tempting, but notice this: the Tsrmls_fetch call requires a certain amount of processing time. This is not obvious in a single iteration, but as your number of threads increases, your extension will show this bottleneck as you call Tsrmls_fetch (). Therefore, please use it with caution.

Note: For compatibility with C + + compilers, be sure to place Tsrmls_fetch () and all variable definitions at the top of a given block scope (before any other statements). Because the Tsrmls_fetch () macro itself has many different parsing methods, it is best to use it as the last line of the variable definition.

Summary

This chapter is primarily an overview of the various concepts that will be explained in subsequent chapters. You should also have a basic understanding of the whole thing, not just to build extensions, but also behind the scenes of the Zend engine and the TSRM layer, which will allow you to monetize PHP when you embed it in your app.

The above is [translation][php extended development and Embedded] 1th Chapter-php life cycle content, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!

  • Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.