PHP7新特性、运行模式和生命周期

1、PHP目录

    PHP源码核心目录 Zend文件下,spai放在sapi的文件下,一些扩展的方法和库函数在ext文件下

    编译的脚本是在configure目录下,接下来make进行编译,make install 会把相关的二进制文件拷贝到我指定的目录下

 ./configure  --prefix=/usr/tmp/php/php-7.1.0  --enable-fpm --enable-debug

 2、PHP7新特性:

  太空船操作符:用于比较2个表达式,例如当a小于,等于或大于a小于,等于或大于b时,分别返回-1,0,1

 echo 1 <=> 1; //0 echo PHP_EOL; echo 1 <=> 2; //-1 echo PHP_EOL; echo 2 <=> 1; //1

  类型声名:declare(strict_types=1);//strict_types=1表示开启严格模式

declare(strict_types=1); //strict_types=1表示严格模式
function sum(int ...$ints){
    return array_sum($ints);
}
var_dump(sum(1,'2','3.1',4.1));

执行结果
Fatal error: Uncaught TypeError: Argument 2 passed to sum() must be of the type integer, string given

  null合并操作符: $page=isset($_GET['page'])?$_GET['page']:0;  等于 $page=$_GET['page']??0;

 常量数组: define('ANIMALS',['dog','cat']);

 批量导入: use Space\{Class A,ClassB,Class as C};

 throwable接口

try{
  undefindfunc();
}catch(Error $e){
var_dump($e);
}
//可以捕获没有定义函数的这个错误

 Clousure:call()   

//在php7可通过call来暂时绑定一个闭包对象到$this对象并调用它
class Test{
 private $num=1;
}
$f=function(){
 return $this->num+1;
}
echo $f->call(new Test);

//在php7之前,当动态的给一个对象添加方法时,可以通过Closure来复制一个闭包对象,并绑定到一个$this对象和类作用域

class People{
    private $age=10;
}
$f=function(){
    return $this->age+1;
};

$p=$f->bindTo(new People,'People');
echo $p();

intdiv函数: intdiv(10,3) //10除以三取整数

list的方括号写法:list($a,$b,$c)=$arr[1,2,31]  等于 [$a,$b,$c]=$arr;

PHP运行原理

1、目前常见的4种PHP运行模式

  1. CGI通用网关接口模式
  2. FAST-CGI模式
  3. CLI命令行模式
  4. 模块模式

CGI通用网关接口模式

   CGI就是将Web服务器和PHP执行程序连接起来,把接受指令传递给PHP执行程序,每有一个用户请求,都会先要创建CGI6的子进程,然后处理请求,用户请求数量非常多会大量挤占系统的资源,造成效率低下,所以有多长连接请求就有多少CGI子进程,子进程反复加载时导致CGI性能低下的主要原因

FAST-CGI模式

是cgi的升级版本,FastCGI 像是一个常驻 (long-live) 型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork 一次,也是一种协议

FastCGI的工作原理是:

  (1)、Web Server启动时载入FastCGI进程管理器【PHP的FastCGI进程管理器是PHP-FPM(php-FastCGI Process Manager)】(IIS ISAPI或Apache Module);

  (2)、FastCGI进程管理器自身初始化,启动多个CGI解释器进程 (在任务管理器中可见多个php-cgi.exe)并等待来自Web Server的连接。

 (3)、当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。

  (4)、FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在 WebServer中)的下一个连接。在正常的CGI模式中,php-cgi.exe在此便退出了。

  在CGI模式中,可以想象 CGI通常有多慢。每一个Web请求PHP都必须重新解析php.ini、重新载入全部dll扩展并重初始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。

CLI命令行模式

一般使用调用脚本、查看php信息时会使用到该模式

php -r"phpinfo();" |less 分页显示

模块模式

  1. Apache + mod_php
  2. lighttp + spawn-fcgi
  3. nginx + PHP-FPM(php在Nginx中运行模式)

运行原理

PHP-CGI:fast-cgi是一种协议,而php-cgi是实现了这种协议的进程。不过这种实现比较烂。它是单进程的,一个进程处理一个请求,处理结束后进程就销毁

PHP - FPM:是对php-cgi的改进版,它直接管理多个php-cgi进程/线程。也就是说,php-fpm是php-cgi的进程管理器因此它也算是fastcgi协议的实现
php的运行原理,就是在服务器启动时,自动载入PHP-FPM进程管理器,从而管理多个PHP-CGI进程来准备响应用户的请求,如下图所示:

由于php-cgi是随服务器启动载入的,所以初始化变量只会发生一次

运行模式和运行原理的区别

多个运行模式相当于超市的不同入口,运行原理就是进入超市后的固定的行走路线,通过不同的运行模式进入到底层(进入超市)

SAPI简介

 SAPI相当于PHP外部环境的代理器。PHP可以应用在终端上(CLI SAPI),也可以应用在Web服务器上(CGI SAPI)

PHP运行的生命周期

  在模块初始化前,首先调用sapi_startup(sapi_module),对sapi_model进行初始化工作,通过调用sapi_model的startup函数(模块启动调用的函数),CLI调用php_cli_startup函数,该函数又调用了php_module_startup函数,也就是对应的模块初始化

   CLI模式生命周期:

     php_module_startup  模块初始化阶段(注册内部扩展、加载外部扩展、启动附加的PHP扩展、启动各个模块、禁用php.ini里面的禁用函数和类)

     php_request_startup  请求初始化阶段 (初始化输出handler的栈,并把OG(FLAGS置为使用)、调用zend_activate(初始化GC、初始化编译器、初始化执行器、初始化扫描器)、对信号进行处理、设置超时时间、初始化相关全局变量)

     php_execute_script    脚本执行阶段(读取的PHP代码进行解析,先词法解析并使词法分析指针指向第一个位置,解析成token,然后语法解析,生成抽象语法树,然后通过对抽象语法树进行遍历生成opcode,opcode在虚拟机上执行得到对应的结果)

    php_request_shutdown 请求关闭阶段(调用各个模块中注册的关闭函数和析构函数、将输出缓冲器重内容输出、调用所有扩展注册的钩子rshutdown函数、销毁request相关的全局变量、关闭编译器和执行器、还原ini配置)(完成这些工作,fpm模式会循环等待请求到来)

    php_module_shutdown 模块关闭阶段(调用sapi_flush() 然后进行销毁所有模块、全局变量、关闭扩展、销毁ini对应的HashTable、关闭ini config、关闭内存管理器、关闭输出output、析构垃圾回收)

FPM

  FPM是一个FastCGI进程管理器,对于5.3之前只是一个补丁包,从5.3开始,PHP集成了PHP-FPM。提供了更好的PHP的进程管理方式,可以有效控制内存和进程,支持平滑重启PHP和重载PHP配置

FPM的三种模式

  pm=static  始终保证一个固定的子进程数,子进程数一般由pm.max_children = 20来指定的,起20个work进程

  pm=dynamic 动态模式,启动的时候生成固定数量子进程,由pm.start_servers = 10来控制,也就是最小的子进程数,最大是                           由pm.max_children 控制,另外还支持两种参数min_spare_servers = 10和max_spare_servers = 20最大和最小闲                         置进程数的最小值和最大数目

  pm=ondemand 按需要的一个模式,和动态模式相反,是按内存放在第一位的,每个闲置进程都会在一定时间内被杀死,这个                          模式的好处是低峰期的话,内存会降下来,弊端就是高峰期频繁创建进程的弊端

网络编程:

 socket   首先通过socket创建一个fd

 bind  设置端口号,使用bind函数进行绑定

 listen  监听

 accept 一般是进入一个死循环,调用accept函数进行接收请求,如果没有请求函数的话会挂起,请求进入阻塞状态,这个时候会把cpu让出节省资源,如果请求来了,accept函数进行接收,然后读取请求 处理自己的逻辑接着返回请求

   

信号处理:

  真正处理请求的是work进程不是master进程,master进程只是起到了管理和监听work进程的作用,当work进程挂掉了会发送信号给master请求,然后一旦请求过来了,master进程会拉起一个work请求进行处理,请求由Nginx的worker进行处理,转出对应的FastCGI,请求FPM,accept由FPM的worker进程处理,执行完毕再返回给Nginx,Nginx再进一步返回给Client,所以:

  当 kill master的时候,php-fpm进程全部退出,都不能工作

  当kill  -9 master的时候,可以工作

  当kill  work进程的时候,可以工作,会发现拉起一个新的work进程

kill就是给某个进程id发送了一个信号。默认发送的信号是SIGTERM,而kill -9发送的信号是SIGKILL,即exit。exit信号不会被系统阻塞,所以kill -9能顺利杀掉进程。

系统会发送一个SIGTERM的信号给对应的程序。当程序接收到该signal后,将会发生以下的事情

  1. 程序立刻停止
  2. 当程序释放相应资源后再停止
  3. 程序可能仍然继续运行

      大部分程序接收到SIGTERM信号后,会先释放自己的资源,然后在停止。但是也有程序可以在接受到信号量后,做一些其他的事情,并且这些事情是可以

      配置的。如果程序正在等待IO,可能就不会立马做出相应。

  也就是说,SIGTERM多半是会被阻塞的、忽略。

FPM模式的生命周期:

 php_module_startup:模块的初始化

 fcgi_accept_request:对请求的接受,进入循环,实际上调用的是accept,阻塞等待请求,如果有请求进来,会被换起,进入php_request_startup,初始化请求。为了防止多个进程对accept进行抢占出现惊群现象,增加了锁机制,但是在Linux的2.6内核上,阻塞版accept不存在惊群现象了

 php_request_startup  请求初始化阶段

 php_execute_script    脚本执行阶段(读取的PHP代码进行解析,先词法解析,解析成token,然后语法解析,生成抽象语法树,然后通过对抽象语法树进行遍历生成opcode,opcode在虚拟机上执行得到对应的结果)

 fpm_request_end:请求的关闭

  php_request_shutdown 请求关闭阶段

  php_module_shutdown 模块关闭阶段

1、因为Fpm是一个常驻内存的这么一个进程,它需要请求完一个并接受下一个请求并处理,所以进行一个循环,当FPM整个退出的时候才需要执行 php_module_shutdown

2、其实FCGI_LOCK/FCGI_UNLOCK在Linux已经没有实现了,因为在Linux2.6内核上,阻塞版本的accept系统调用不存在惊群了,当新的连接过来时,大家会发现,仅有一个子进程返回新建的链接,其他子进程继续休眠在accept调用上,没有惊群现象

3、 主进程创建了socket、bind、listen之后,fork()出来多个进程,worker进程会进入循环(accept)这个listen_fd,当没有请求的时候,会阻塞在fcgin_accept_request(accept),让出cpu资源,成为空闲进程,当请求到达时会有一个worker进程抢到并处理进入FastCGI的处理阶段

4、历史上,Linux的accpet存在惊群问题,但现在2.6以后的内核都解决该问题了。即,当多个进程/线程都阻塞在对同一个socket的接受调用上时,当有一个新的连接到来,内核只会唤醒一个进程,其他进程保持休眠,压根就不会被唤醒。

  主进程创建了socket、bind、listen之后,fork()出来多个进程,每个子进程都开始循环处理(accept)这个listen_fd。每个进程都阻塞在accept上,当一个新的连接到来时候,所有的进程都会被唤醒,但是其中只有一个进程会接受成功,其余皆失败,重新休眠。

FastCGI协议:

Nginx收到HTTP请求,然后Nginx直接把HTTP协议的包进行解包然后做一次封装,然后转发给PHP-FPM,一般PHP-FPM监听9000端口,Nginx向PHP-FPM发出的请求就是FastCGI编码的,这里是一个个的键值对,又有头部和尾部的一个编码,然后PHP-FPM对FastCGI的协议的一个解码,然后走到FPM的生命周期里面,然后转到index.php或者其他PHP文件里面,然后进行脚本的执行,执行完以后由合成一个FastCGI协议返送给Nginx,返回的时候比较简单一些,按照HTTP协议的方式进行内容编码,但是还是FastCGI协议,Nginx再解析成HTTP协议发送给客户端

FastCGI协议两部分:第一部分传入到Nginx传入FPM 注入协议

总结:

   整个FPM模式实际上是一个多进程模式,首先由calling process进程fork出master进程,然后master进程会创建Socket,然后fork出worker进程,worker进程会在accept处阻塞等待,请求过来时,由其中一个worker进程处理,按照FastCGI模式进行各阶段读取,另外,FPM建立计分板机制,关注全局和每个woker工作情况,方便使用者监控

发布了83 篇原创文章 · 获赞 87 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/ligupeng7929/article/details/89709302