PHP0_相关概念梳理与环境搭建

进程与进程树

当在系统中运行一个程序后,会产生一个进程。是什么把程序进程创建出来的?
可以理解为系统的调度进程,创建了程序进程。
那么,系统调度进程又是谁创建出来的?
在系统中,产生新的进程只有通过从父进程(fork)的方式。 pid为0的进程为 init进程(祖先进程) 由内核创建。 pid为0的进程无法被 kill。

进程pid

pid是进程编号,系统中的每个进程都有唯一编号,并且编号是有限的,如果进程号都用光了则无法创建新的进程。

进程树

根据上述描述,进程必须由父进程 fork() 而产生,所以就可以通过树状结构来描述进程的结构。
如: cmd -> notepad.exe
则: cmd 为 notepad.exe的父进程.

在任务管理器中,有个叫 结束进程树的功能,表示结束该进程,同时结束由该进程所创建的所有子进程。
当对 cmd 结束进程树的时候,notepad.exe的进程也会被结束

孤儿进程、僵尸进程

僵尸进程

僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用 wait或waitpid 获取子进程的状态信息,那么子进程的进程描述符(pid)仍然保存在系统中,无法释放被重新使用。这种进程称之为僵尸进程。

在 Linux 中执行 ps 命令,可以看到进程列表,也可以看到进程的状态。
正常状态: R(运行状态)、S(睡眠状态)、D(磁盘休眠状态)、T(停止状态)、X(死亡状态)
僵尸状态: Z(僵尸进程)

僵尸进程无法用kill命令干掉,因为kill命令只是用来终止进程的,而僵尸进程已经终止。僵尸进程会一直占用pid资源,过多的僵尸进程会导致系统无pid使用,从而无法创建新的进程,危害极大

任何一个子进程终止之后,并非马上就消失,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。

如果子进程在终止之后,父进程没有及时处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。

如果父进程在子进程结束之前退出,则子进程将由init进程接管变成孤儿进程,下文有说明

wait()/waitpid()父进程清理子进程,释放PID
进程一旦调用了 wait 就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后 return;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。waitpid()比 wait() 功能更全一些,需要的时候可以搜索了解

wait()会一直阻塞?那么父进程不就无法处理别的工作了吗?

父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一下,程序实现复杂。

其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

上述说的这些开辟进程(fork、wait)都是底层基础函数,使用其他高级语言,如:Java、Python开辟进程的时候,他们都封装好了API供我们调用,所以不用操心僵尸进程的产生。

孤儿进程

一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(祖先进程)所收养,并由init进程对它们进行托管,所以孤儿进程不会成为僵尸进程。也没有什么危害。
init进程会循环的 wait()子进程,保证不让任何子进程成为僵尸进程,所以孤儿进程被 init 进程收养,还是很有保障的

Web服务器的概念

Web服务器通过监听端口,通过HTTP协议来与客户端通信。
通常 Web服务器会监听80端口,接收来自浏览器等客户端的HTTP请求,反馈响应报文。
Web服务器有很多,分别适合不同的应用场景,常见的Web服务器有:Apache、Nginx、Tomcat等

Apache、Nginx对比

Apache 的优势

  1. apache的rewrite比nginx强大,在rewrite(重写/转发/重定向)频繁的情况下,用apache,支持各种规则配置
  2. apache发展到现在,模块超多,基本想到的都可以找到,所以可以做的事更多
  3. apache更为成熟,少bug,nginx的bug相对较多
  4. apache更稳定
  5. apache模块多,对PHP支持比较简单,nginx相对麻烦一些
  6. apache在处理动态请求有优势(模块多,可以通过加载不同的模块,处理各种不同语言的动态请求),nginx在这方面是鸡肋,本身不支持动态请求处理,需要更多的组件与配置才有可能支持。

Nginx优点

  1. 轻量级,同样起web服务,比apache 占用更少的内存及资源;
  2. 抗并发,高吞吐量,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能;
  3. 高度模块化的设计,编写模块相对简单;
  4. 社区活跃,各种高性能模块发布迅速。

总结

Nginx 静态处理性能比 Apache 高 3倍以上
Apache对PHP支持比较简单,Nginx 需要配合其他后端组件(PHP-FPM),配置相对麻烦一些。

所以现在针对一些大型的系统,通常都是 nginx 做转发服务,多个apache 处理动态请求,然后返回给nginx 。也就是 负载均衡,横向扩展更多的 apache + php 则可以提高系统吞吐量与并发。

如果单台服务器的话,推荐使用 Nginx + PHP + PHP-FPM(后面会讲到)

为什么Nginx吞吐量高?

因为 Nginx 对待请求是 异步非阻塞的,而 Apache 是 同步阻塞的。
所以 Nginx 吞吐量高,但相对不稳定,而 Apache 是相对稳定,吞吐量相对低。

阻塞
程序执行的时候,遇到耗时操作(IO),等待耗时执行完毕才继续向下执行,是 阻塞。

非阻塞
程序执行的时候,遇到耗时操作(IO),不等待其完成,后续的代码可以继续执行,是 非阻塞


同步
程序执行的时候,遇到耗时操作(IO),等待耗时操作反馈结果,再继续向下执行,是同步。

异步
程序(主线程)执行的时候,遇到耗时操作(IO),开启新的线程,处理耗时操作,主线程继续执行代码,各干各的,等 耗时操作 获得了结果,再通知主线程,拿到结果;是异步

异步的最常见的实现是: 消息模型;(线程间的通信)

Apache 的工作模式

1. beos 工作模式
beos工作模式(跟linux关系不大,或者暂时用不上)

在Beos系统上的工作模式,使用一个单独的控制线程来创建和控制处理请求的工作线程。

2. event工作模式
(不太稳定,或者说暂时用不上)

event模式由于把服务进程从链接中分离出来,在开启KeepAlive场合下相对worker模式能够承受的了更高的负载。event模式为 worker开发的变种模式,配置以及指令与worker完全相同。不过event模式不能很好的支持https的访问,有时还会出现一系列的问题。

3. worker工作模式
(与php配合不好,或者说暂时用不上)

worker模式由于使用线程来进行处理请求,所以能够处理海量请求,而系统资源的开销要小于基于进程的服务器。同时worker模式也使用了多进程,每个进程又有着多个线程,以获得基于进程服务器的稳定性。

4. mpmt_os2工作模式
(很少用,或者说暂时用不上)

mpmt_os2是专门针对OS/2优化过的混合多进程多线程多路处理模块(MPM) 。

5. prefork工作模式
Linux:(本篇文章的主角,使用最多而且最稳定的工作模式)

prefork工作模式是linux下apache安装时候的默认工作模式,是使用最普遍的工作模式。为了能够简单的明白他的工作原理,下面是一个过程:

有一台正在运行的apache服务器,用户A访问该apache的时候apache建立一个新的进程1处理用户A的请求。

这时又有一个用户B访问该apache,apache又建立一个新的进程2处理用户B的请求。

后来又有用户C,D,E访问该apache,apache又建立三个进程3,4,5处理他们的请求。

如果每当一个新用户访问该apache,apache再建立一个新的进程处理用户的请求,是不是太慢了呢?

所以apache的prefork模式在apache第一次启动的时候就建立5个进程,等待用户的连接请求,有一个用户访问,就有一个进程处理他的请求。

那么如果有5个用户同时访问apache,apache第一次建立的5个进程全部用光了,所以apache就再从新在建立5个进程,等待下一批用户的请求。

当请求处理完毕后,进程会被关闭,释放资源。prefork模式会根据服务器的硬件情况,设定apache最多只能同时建立256个进程。

当并发>= 256个进程的时候,未排上队的请求就会被阻塞等待,同步等待有空闲的进程处理完毕后,再进入队列中处理。

(所以apache 是同步阻塞的)

Windows下的 Apache 是多线程非多进程的!

由于在 Windows 下 prefork工作模式 创建进程比较耗费资源(Win进程方式的缺陷),所以在 Windows 下 Apache 是 多线程而非 多进程!

所以当你下载Win版本的php的时候,会让你选择线程安全版本和非线程安全版本。 这个我们后面也会说到!

Nginx 的工作模式

Nginx 是多线程工作模式,而非多进程;
nginx 接收一个请求后,不会等待这个请求处理完毕拿到结果后再接收下一个请求,而是异步处理,然后马上循环接收下一个请求(不阻塞)

请求处理 到 拿到结果 这个过程是异步的,拿到结果后会主动 通知Nginx;
这样做,每个请求过来就不需要等待很长的时间排队,而是马上就能接收,开始进行处理了。等处理完成之后,会主动通知回调这个线程进行数据返回。 这样的工作模式,具有超高的吞吐量

(所以 Nginx 是异步非阻塞的)

What Is CGi、FastCgi、PHP-FPM?

CGi

通用网关接口,可以理解它为处理动态请求的老祖宗!
最早的时候,互联网只能返回静态页面,后来出现了 CGI , 再后来就有人使用 CGi 技术来写动态网站;

如:

http://guodongxiaren.me/cgi-bin/helloworld.cgi

CGi可以使用 C/C++,Perl,VB,DELPHI,unix/linux shell,python等一切支持标准输入输出流的语言进行开发;
Apache 有个模块支持 Cgi,所以你可以写一个 Cgi 程序托管到 Apache 体验一下,具体流程就不再赘述,可以百度;
(Nginx 不支持传统Cgi)

Cgi工作模式: 客户端http请求报文流 => Apache => Cgi => 标准输入拿到流 => 拼凑协议成输出流 => 返回客户端;

也有一些语言为Cgi扩展了库,让其不用手动拼凑协议,封装了一些常用的函数;但Cgi还是没有流行起来;因为每次HTTP请求CGI,Web服务器都有启动一个新的进程去执行这个CGI程序,即颇具Unix特色的fork-and-execute。当用户请求量大的时候,这个fork-and-execute的操作会严重拖慢Web服务器的性能。

时势造英雄,FastCGI(简称FCGI)技术应运而生。简单来说,其本质就是一个常驻内存的进程池技术,由调度器负责将传递过来的请求发送给 进程池中的进程,在一个请求处理完成之后,该处理进程不销毁,继续等待下一个请求的到来。

FastCgi

以PHP的 PHP-FPM 为例:

如上图:

  1. Apache 和 Nginx 都支持 FastCgi 这种工作方式,不过他们的配置方式不同
  2. 当请求到了 Apache/Nginx,Apache/Nginx 会将请求转发给 FastCgi
  3. FastCgi 调度器调度进程池,解析请求后处理请求,返回给 Apache/Nginx
  4. Apache / Nginx 反馈结果给客户端

需要理解的是:
FastCgi 是独立的程序,它监听着端口,等待着请求; 所以它可以部署在和 Apache/Nginx 同一个机器上,也可以分开分布式部署;

Nginx/Apache 有点像客户端,把从浏览器拿到的请求流,通过 Tcp/ip 技术发送给 FastCgi,所以不管FastCgi部署在哪台机器上,只要他们网络通常,就能正常工作。

Nginx/Apache 与 FastCgi,也支持 Unix Socket(同主机进程间通信),但是要求 Nginx/Apache 与 FastCgi 必须在同一台机器上;

其实,FastCgi 并不是一个产品,而是一个标准,很多常见的FastCgi 产品有:

  1. PHP-FPM,支持PHP解析的 FastCgi
  2. spawn-fcgi,支持Python解析的 FastCgi

(只要支持FastCgi标准,都可以和 Nginx/Apache 组合使用;根据不同的实现,来解析不同语言的服务器端代码,完成请求的动态处理)

PHP-FPM


# Nginx 配置 FastCgi ,当请求网站下 .php文件的时候,反向代理转发给php-fpm
location ~ \.php$ {
        root html/wordpress;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
        #fastcgi_cache fastcgi_cache_zone;                     #指定缓存区名称
        #fastcgi_cache_key $host$request_uri;                  #定义缓存的key,根据md5值为缓存文件名
        #fastcgi_cache_valid 200 302 10m;
        #fastcgi_cache_valid 301 1d;
        #fastcgi_cache_valid any 1m;
}

## -------------------------------------------------------

# Apache 配置 FastCgi 
# 1. 配置 httpd.conf,加载支持 cgi 请求转发模块
LoadModule proxy\_module modules/mod\_proxy.so  
LoadModule proxy\_fcgi\_module modules/mod\_proxy\_fcgi.so

#2. 将.php 转发给 php-f
ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://php-fpm:9000/v/php-fpm/www/$1

from: https://www.jianshu.com/p/93e2cbdfd209 ; 更多查百度

上文大概讲解了FastCGi 的概念,现在我们具体来说一说 PHP5.3后,自带的 FastCgi 实现, PHP-FPM;

php-fpm 是进程管理器,php-fpm 实现了 fastcgi 协议,用来管理 php-cgi 的;
php-cgi 是解释 php 程序的,是PHP的解析器;

php-fpm 会先启动一个 master 进程,解析配置文件(php.ini ),初始化执行环境,然后再启动多个 worker,当请求过来时,master 会传递给一个 worker,处理请求,然后worker进程不关闭,等待下一个请求。

php-fpm 可以不用重启变更配置,但平滑更新配置的原因是这样的:
当 php.ini 的配置变更后,正在工作的 worker进程还是使用老的配置(下次工作,将会使用新配置); 新的请求来的时候,交给一个worker进程时,会把新的配置同时交给worker,这个worker进程工作的时候,就是使用新的配置来解析与执行PHP代码;

PHP-FPM现在已非常成熟, 支持 Nginx + PHP-FPM 也支持 Apache + PHP-FPM

Nginx + PHP Or Apache + PHP ?

Nginx + PHP + PHP-FPM已成为当前流行的搭配环境了

  1. Nginx 吞吐量高
  2. PHP-FPM已很成熟了
  3. PHP-FPM是可以分布式部署的,TCP/Unix socket 通信的,所以支持横向扩展更好一些;

A: Apache + PHP : Load Module模式
将PHP作为 Apache 的 Module可以方便快速的搭建起 PHP环境,将PHP作为一个 .so(.dll) 动态库,配置给 Apache ,成为Apache 的扩展,相当于Apache 具有解析 PHP的能力,不过这种方式,有相应的缺陷;

1.Apache 和 PHP合体,如果PHP的配置修改后,需要重启Apache 才支持新的配置

2.前文讲过,Apache 在Windows 下,工作方式是当一个请求过来,是开启线程处理而非进程,所以要选择 线程安全版本的 PHP,但是线程安全,PHP代码的执行解析效率就会差一些! [ Apache处理多个php请求,Win下开辟多个线程,每个线程都调用 Apache的PHP扩展 (动态链接库) ,解析PHP脚本,返回结果,如果PHP扩展是非线程安全的,就会造成线程安全问题,所以在Win下,使用 Load Module 的组合模式要选择线程安全版本]

B: Apache + PHP-FPM: FastCgi模式
Apache 也支持将 php 请求转发给 PHP-FPM,配置方式和 Nginx 不同;
from: https://www.jianshu.com/p/93e... ; 更多查百度;

FastCgi模式是使用进程解析请求,所以不存在线程安全的问题,不管是在Windows 还是Linux下都可以使用非线程安全版的PHP

Win环境配置(手动)

Nginx + PHP

Apache + PHP

资源下载

Apache:
Win版本的Apache,官网已不提供二进制下载了,需要下载源码自己编译;不过还有一些第三方网站提供着 Apache Windows Bin 的下载;
(推荐) https://www.apachelounge.com/
https://www.apachehaus.com/cg...


httpd-2.4.41-win64-VC15.zip
2.4.41版本的 64位 Apache ,使用 VC15编译而成,如果想要正常运行,请安装 VC15 运行库,不然可能会出现问题! 可以在官网如下位置,下载对应的运行库;
image.png


Win下的问题: 线程安全还是非线程安全?

上文中 《Nginx + PHP Or Apache + PHP : Apache + PHP : Load Module模式》已经阐述过这个问题了。

  1. Win + Apache + PHP + Load Module 工作模式: 线程安全
  2. Win + Apache + PHP + PHP-FPM 工作模式: 非线程安全
  3. Win + Nginx + PHP + PHP-FPM 工作模式: 非线程安全

在Linux 下,不管是 Apache + PHP 还是 Apache + PHP-FPM 都是多进程的形式工作的,所以就不用担心线程安全了!

Linux 环境配置(手动)

Nginx + PHP

Apache + PHP

源码安装、二进制安装包安装(包管理器)、集成开发环境安装

  1. 集成环境 (优点: 方便;缺点: 不可以选择 Apache 、Mysql、PHp的版本,不可以进行功能定制,所有的模块与功能基本都安装了)
  2. 源码手动安装(生产服务器会用的到) ,这种方式就类似于定制衣服,会比较的合身 。 现场编译的方式,编译器会根据当前的 服务器硬件、服务器系统环境进行相关的编译优化,编译出来的环境更适合当前硬件,运行速度将会更优秀一些。 (优点:效率高、可以定制、可以选择模块; 缺点: 配置麻烦)
  3. rpm 包的形式 (是二进制安装包)通过 yum 、apt 、brew 可以非常方便的管理包以及包的依赖关系 (优点: 可以定制版本,但不可以定制功能;)

源码包安装比较耗时,大约需要3天左右,因为 Apache 是模块化的,编译的时候 要根据系统选择模块, PHP也是模块化的,编译的时候也需要根据系统所需要的模块进行编译。 那为什么不能加载所有模块那? 第一是加载多的模块更消耗资源 , 第二是加载多的模块就增加了安全风险,产生漏洞的风险更大。

阅读 428

推荐阅读