PHP
11/08
13
首先,设置nginx最新稳定版本的源,还有php-fpm的源
sudo gedit /etc/apt/sources.list
加入源
deb http://ppa.launchpad.net/nginx/stable/ubuntu lucid main
deb http://ppa.launchpad.net/brianmercer/php/ubuntu lucid main
deb-src http://ppa.launchpad.net/brianmercer/php/ubuntu lucid main
然后加入Key,并更新:
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 8D0DC64F
sudo apt-get update
首先, 安装最新稳定版本版本的Nginx
apt-get install nginx
nginx安装好, 我们测试一下OK没
sudo /etc/init.d/nginx start
访问下: http://localhost, OK, 可以访问
注: 如果没有/var/www目录大话,自己创建一个就可以了
然后我们安装php5.3和相关一堆东西
sudo apt-get install php5-cgi php5-mysql php5-curl php5-gd php5-idn php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-mhash php5-ming php5-ps php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl php5-json php5-suhosin php5-common php-apc php5-dev
接着安装php5-cgi和 php5-fpm
sudo apt-get install php5-fpm php5-cgi
启动CGI
/etc/init.d/php5-fpm start
接着我们配置nginx: sudo gedit /etc/nginx/site-available/default
我的配置如下:
server {
listen 80;
server_name localhost;
root /var/www;
access_log /var/log/nginx/localhost.access.log;
error_page 404 /404.html;
location / {
index index.html index.htm index.php;
}
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
access_log off;
expires 30d;
root /var/www;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_pass backend;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name;
include fastcgi_params;
fastcgi_intercept_errors on;
fastcgi_ignore_client_abort off;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
}
location ~ /\.ht {
deny all;
}
}
upstream backend {
server 127.0.0.1:9000;
}
配置站点2
sudo cp default arlicle
然后将server_name改为:www.arlicle.com arlicle.com;
对应的目录也进行修改 然后执行下面大命令启动arlicle 站点
sudo ln -s /etc/nginx/sites-available/arlicle /etc/nginx/sites-enabled/arlicle
sudo /etc/init.d/nginx reload
访问测试一下,正常.
最后我们安装加速引擎:
sudo pecl install memcache
sudo apt-get install libpcre3-dev
sudo pecl install apc
注:
将phpmyadmin加入访问:
ln -s /usr/share/phpmyadmin /var/www/phpmyadmin 这样就可以访问了
设置nginx上传文件大小: /etc/nginx/nginx.conf
clientmaxbody_size 30M;
修改php.ini文件: /etc/php5/fpm
postmaxsize = 30M
配置nginx 访问php时不存在php的404报错, 在配置文件中加入
error_page 404 /404.html;
sudo gedit /etc/apt/sources.list
加入源
deb http://ppa.launchpad.net/nginx/stable/ubuntu lucid main
deb http://ppa.launchpad.net/brianmercer/php/ubuntu lucid main
deb-src http://ppa.launchpad.net/brianmercer/php/ubuntu lucid main
然后加入Key,并更新:
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 8D0DC64F
sudo apt-get update
首先, 安装最新稳定版本版本的Nginx
apt-get install nginx
nginx安装好, 我们测试一下OK没
sudo /etc/init.d/nginx start
访问下: http://localhost, OK, 可以访问
注: 如果没有/var/www目录大话,自己创建一个就可以了
然后我们安装php5.3和相关一堆东西
sudo apt-get install php5-cgi php5-mysql php5-curl php5-gd php5-idn php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-mhash php5-ming php5-ps php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl php5-json php5-suhosin php5-common php-apc php5-dev
接着安装php5-cgi和 php5-fpm
sudo apt-get install php5-fpm php5-cgi
启动CGI
/etc/init.d/php5-fpm start
接着我们配置nginx: sudo gedit /etc/nginx/site-available/default
我的配置如下:
server {
listen 80;
server_name localhost;
root /var/www;
access_log /var/log/nginx/localhost.access.log;
error_page 404 /404.html;
location / {
index index.html index.htm index.php;
}
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
access_log off;
expires 30d;
root /var/www;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_pass backend;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name;
include fastcgi_params;
fastcgi_intercept_errors on;
fastcgi_ignore_client_abort off;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
}
location ~ /\.ht {
deny all;
}
}
upstream backend {
server 127.0.0.1:9000;
}
配置站点2
sudo cp default arlicle
然后将server_name改为:www.arlicle.com arlicle.com;
对应的目录也进行修改 然后执行下面大命令启动arlicle 站点
sudo ln -s /etc/nginx/sites-available/arlicle /etc/nginx/sites-enabled/arlicle
sudo /etc/init.d/nginx reload
访问测试一下,正常.
最后我们安装加速引擎:
sudo pecl install memcache
sudo apt-get install libpcre3-dev
sudo pecl install apc
注:
将phpmyadmin加入访问:
ln -s /usr/share/phpmyadmin /var/www/phpmyadmin 这样就可以访问了
设置nginx上传文件大小: /etc/nginx/nginx.conf
clientmaxbody_size 30M;
修改php.ini文件: /etc/php5/fpm
postmaxsize = 30M
配置nginx 访问php时不存在php的404报错, 在配置文件中加入
error_page 404 /404.html;
10/08
24
使用PHP真正的多进程运行模式,适用于数据采集、邮件群发、数据源更新、tcp服务器等环节。
PHP有一组进程控制函数(编译时需要 –enable-pcntl与posix扩展),使得php能在*nix系统中实现跟c一样的创建子进程、使用exec函数执行程序、处理信号等功能。 PCNTL使用ticks来作为信号处理机制(signal handle callback mechanism),可以最小程度地降低处理异步事件时的负载。何谓ticks?Tick 是一个在代码段中解释器每执行 N 条低级语句就会发生的事件,这个代码段需要通过declare来指定。
常用的PCNTL函数
1. pcntl_alarm ( int $seconds )
设置一个$seconds秒后发送SIGALRM信号的计数器
2. pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] )
为$signo设置一个处理该信号的回调函数。下面是一个隔5秒发送一个SIGALRM信号,并由signal_handler函数获取,然后打印一个“Caught SIGALRM”的例子:
<?php
declare(ticks = 1);
function signal_handler($signal) {
print "Caught SIGALRM\n";
pcntl_alarm(5);
}
pcntl_signal(SIGALRM, "signal_handler", true);
pcntl_alarm(5);
for(;;) {
}
?>
declare(ticks = 1);
function signal_handler($signal) {
print "Caught SIGALRM\n";
pcntl_alarm(5);
}
pcntl_signal(SIGALRM, "signal_handler", true);
pcntl_alarm(5);
for(;;) {
}
?>
3. pcntl_exec ( string $path [, array $args [, array $envs ]] )
在当前的进程空间中执行指定程序,类似于c中的exec族函数。所谓当前空间,即载入指定程序的代码覆盖掉当前进程的空间,执行完该程序进程即结束。
<?php
$dir = '/home/shankka/';
$cmd = 'ls';
$option = '-l';
$pathtobin = '/bin/ls';
$arg = array($cmd, $option, $dir);
pcntl_exec($pathtobin, $arg);
echo '123'; //不会执行到该行
?>
$dir = '/home/shankka/';
$cmd = 'ls';
$option = '-l';
$pathtobin = '/bin/ls';
$arg = array($cmd, $option, $dir);
pcntl_exec($pathtobin, $arg);
echo '123'; //不会执行到该行
?>
4. pcntl_fork ( void )
为当前进程创建一个子进程,并且先运行父进程,返回的是子进程的PID,肯定大于零。在父进程的代码中可以用 pcntl_wait(&$status)暂停父进程知道他的子进程有返回值。注意:父进程的阻塞同时会阻塞子进程。但是父进程的结束不影响子进程的运行。
父进程运行完了会接着运行子进程,这时子进程会从执行pcntl_fork()的那条语句开始执行(包括此函数),但是此时它返回的是零(代表这是一个子进程)。在子进程的代码块中最好有exit语句,即执行完子进程后立即就结束。否则它会又重头开始执行这个脚本的某些部分。
注意两点:
1. 子进程最好有一个exit;语句,防止不必要的出错;
2. pcntl_fork间最好不要有其它语句,例如:
<?php
$pid = pcntl_fork();
//这里最好不要有其他的语句
if ($pid == -1) {
die('could not fork');
} else if ($pid) {
// we are the parent
pcntl_wait($status); //Protect against Zombie children
} else {
// we are the child
}
$pid = pcntl_fork();
//这里最好不要有其他的语句
if ($pid == -1) {
die('could not fork');
} else if ($pid) {
// we are the parent
pcntl_wait($status); //Protect against Zombie children
} else {
// we are the child
}
5. pcntl_wait ( int &$status [, int $options ] )
阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。使用$status返回子进程的状态码,并可以指定第二个参数来说明是否以阻塞状态调用:
1. 阻塞方式调用的,函数返回值为子进程的pid,如果没有子进程返回值为-1;
2. 非阻塞方式调用,函数还可以在有子进程在运行但没有结束的子进程时返回0。
6. pcntl_waitpid ( int $pid , int &$status [, int $options ] )
功能同pcntl_wait,区别为waitpid为等待指定pid的子进程。当pid为-1时pcntl_waitpid与pcntl_wait 一样。在pcntl_wait和pcntl_waitpid两个函数中的$status中存了子进程的状态信息,这个参数可以用于 pcntl_wifexited、pcntl_wifstopped、pcntl_wifsignaled、pcntl_wexitstatus、 pcntl_wtermsig、pcntl_wstopsig、pcntl_waitpid这些函数。
例如:
<?php
$pid = pcntl_fork();
if($pid) {
pcntl_wait($status);
$id = getmypid();
echo "parent process,pid {$id}, child pid {$pid}\n";
}else{
$id = getmypid();
echo "child process,pid {$id}\n";
sleep(2);
}
?>
$pid = pcntl_fork();
if($pid) {
pcntl_wait($status);
$id = getmypid();
echo "parent process,pid {$id}, child pid {$pid}\n";
}else{
$id = getmypid();
echo "child process,pid {$id}\n";
sleep(2);
}
?>
子进程在输出child process等字样之后sleep了2秒才结束,而父进程阻塞着直到子进程退出之后才继续运行。
7. pcntl_getpriority ([ int $pid [, int $process_identifier ]] )
取得进程的优先级,即nice值,默认为0,在我的测试环境的linux中(CentOS release 5.2 (Final)),优先级为-20到19,-20为优先级最高,19为最低。(手册中为-20到20)。
8. pcntl_setpriority ( int $priority [, int $pid [, int $process_identifier ]] )
设置进程的优先级。
9. posix_kill
可以给进程发送信号
10. pcntl_singal
用来设置信号的回调函数
当父进程退出时,子进程如何得知父进程的退出
当父进程退出时,子进程一般可以通过下面这两个比较简单的方法得知父进程已经退出这个消息:
1. 当父进程退出时,会有一个INIT进程来领养这个子进程。这个INIT进程的进程号为1,所以子进程可以通过使用getppid()来取得当前父进程的pid。如果返回的是1,表明父进程已经变为INIT进程,则原进程已经推出。
2. 使用kill函数,向原有的父进程发送空信号(kill(pid, 0))。使用这个方法对某个进程的存在性进行检查,而不会真的发送信号。所以,如果这个函数返回-1表示父进程已经退出。
除了上面的这两个方法外,还有一些实现上比较复杂的方法,比如建立管道或socket来进行时时的监控等等。
PHP多进程采集数据的例子
<?php
/**
* Project: Signfork: php多线程库
* File: Signfork.class.php
*/
class Signfork{
/**
* 设置子进程通信文件所在目录
* @var string
*/
private $tmp_path='/tmp/';
/**
* Signfork引擎主启动方法
* 1、判断$arg类型,类型为数组时将值传递给每个子进程;类型为数值型时,代表要创建的进程数.
* @param object $obj 执行对象
* @param string|array $arg 用于对象中的__fork方法所执行的参数
* 如:$arg,自动分解为:$obj->__fork($arg[0])、$obj->__fork($arg[1])...
* @return array 返回 array(子进程序列=>子进程执行结果);
*/
public function run($obj,$arg=1){
if(!method_exists($obj,'__fork')){
exit("Method '__fork' not found!");
}
if(is_array($arg)){
$i=0;
foreach($arg as $key=>$val){
$spawns[$i]=$key;
$i++;
$this->spawn($obj,$key,$val);
}
$spawns['total']=$i;
}elseif($spawns=intval($arg)){
for($i = 0; $i < $spawns; $i++){
$this->spawn($obj,$i);
}
}else{
exit('Bad argument!');
}
if($i>1000) exit('Too many spawns!');
return $this->request($spawns);
}
/**
* Signfork主进程控制方法
* 1、$tmpfile 判断子进程文件是否存在,存在则子进程执行完毕,并读取内容
* 2、$data收集子进程运行结果及数据,并用于最终返回
* 3、删除子进程文件
* 4、轮询一次0.03秒,直到所有子进程执行完毕,清理子进程资源
* @param string|array $arg 用于对应每个子进程的ID
* @return array 返回 array([子进程序列]=>[子进程执行结果]);
*/
private function request($spawns){
$data=array();
$i=is_array($spawns)?$spawns['total']:$spawns;
for($ids = 0; $ids<$i; $ids++){
while(!($cid=pcntl_waitpid(-1, $status, WNOHANG)))usleep(30000);
$tmpfile=$this->tmp_path.'sfpid_'.$cid;
$data[$spawns['total']?$spawns[$ids]:$ids]=file_get_contents($tmpfile);
unlink($tmpfile);
}
return $data;
}
/**
* Signfork子进程执行方法
* 1、pcntl_fork 生成子进程
* 2、file_put_contents 将'$obj->__fork($val)'的执行结果存入特定序列命名的文本
* 3、posix_kill杀死当前进程
* @param object $obj 待执行的对象
* @param object $i 子进程的序列ID,以便于返回对应每个子进程数据
* @param object $param 用于输入对象$obj方法'__fork'执行参数
*/
private function spawn($obj,$i,$param=null){
if(pcntl_fork()===0){
$cid=getmypid();
file_put_contents($this->tmp_path.'sfpid_'.$cid,$obj->__fork($param));
posix_kill($cid, SIGTERM);
exit;
}
}
}
?>
/**
* Project: Signfork: php多线程库
* File: Signfork.class.php
*/
class Signfork{
/**
* 设置子进程通信文件所在目录
* @var string
*/
private $tmp_path='/tmp/';
/**
* Signfork引擎主启动方法
* 1、判断$arg类型,类型为数组时将值传递给每个子进程;类型为数值型时,代表要创建的进程数.
* @param object $obj 执行对象
* @param string|array $arg 用于对象中的__fork方法所执行的参数
* 如:$arg,自动分解为:$obj->__fork($arg[0])、$obj->__fork($arg[1])...
* @return array 返回 array(子进程序列=>子进程执行结果);
*/
public function run($obj,$arg=1){
if(!method_exists($obj,'__fork')){
exit("Method '__fork' not found!");
}
if(is_array($arg)){
$i=0;
foreach($arg as $key=>$val){
$spawns[$i]=$key;
$i++;
$this->spawn($obj,$key,$val);
}
$spawns['total']=$i;
}elseif($spawns=intval($arg)){
for($i = 0; $i < $spawns; $i++){
$this->spawn($obj,$i);
}
}else{
exit('Bad argument!');
}
if($i>1000) exit('Too many spawns!');
return $this->request($spawns);
}
/**
* Signfork主进程控制方法
* 1、$tmpfile 判断子进程文件是否存在,存在则子进程执行完毕,并读取内容
* 2、$data收集子进程运行结果及数据,并用于最终返回
* 3、删除子进程文件
* 4、轮询一次0.03秒,直到所有子进程执行完毕,清理子进程资源
* @param string|array $arg 用于对应每个子进程的ID
* @return array 返回 array([子进程序列]=>[子进程执行结果]);
*/
private function request($spawns){
$data=array();
$i=is_array($spawns)?$spawns['total']:$spawns;
for($ids = 0; $ids<$i; $ids++){
while(!($cid=pcntl_waitpid(-1, $status, WNOHANG)))usleep(30000);
$tmpfile=$this->tmp_path.'sfpid_'.$cid;
$data[$spawns['total']?$spawns[$ids]:$ids]=file_get_contents($tmpfile);
unlink($tmpfile);
}
return $data;
}
/**
* Signfork子进程执行方法
* 1、pcntl_fork 生成子进程
* 2、file_put_contents 将'$obj->__fork($val)'的执行结果存入特定序列命名的文本
* 3、posix_kill杀死当前进程
* @param object $obj 待执行的对象
* @param object $i 子进程的序列ID,以便于返回对应每个子进程数据
* @param object $param 用于输入对象$obj方法'__fork'执行参数
*/
private function spawn($obj,$i,$param=null){
if(pcntl_fork()===0){
$cid=getmypid();
file_put_contents($this->tmp_path.'sfpid_'.$cid,$obj->__fork($param));
posix_kill($cid, SIGTERM);
exit;
}
}
}
?>
php在pcntl_fork()后生成的子进程(通常为僵尸进程)必须由pcntl_waitpid()函数进行资源释放。但在 pcntl_waitpid()不一定释放的就是当前运行的进程,也可能是过去生成的僵尸进程(没有释放);也可能是并发时其它访问者的僵尸进程。但可以使用posix_kill($cid, SIGTERM)在子进程结束时杀掉它。
子进程会自动复制父进程空间里的变量。
PHP多进程编程示例2
<?php
//.....
//需要安装pcntl的php扩展,并加载它
if(function_exists("pcntl_fork")){
//生成子进程
$pid = pcntl_fork();
if($pid == -1){
die('could not fork');
}else{
if($pid){
$status = 0;
//阻塞父进程,直到子进程结束,不适合需要长时间运行的脚本,可使用pcntl_wait($status, 0)实现非阻塞式
pcntl_wait($status);
// parent proc code
exit;
}else{
// child proc code
//结束当前子进程,以防止生成僵尸进程
if(function_exists("posix_kill")){
posix_kill(getmypid(), SIGTERM);
}else{
system('kill -9'. getmypid());
}
exit;
}
}
}else{
// 不支持多进程处理时的代码在这里
}
//.....
?>
//.....
//需要安装pcntl的php扩展,并加载它
if(function_exists("pcntl_fork")){
//生成子进程
$pid = pcntl_fork();
if($pid == -1){
die('could not fork');
}else{
if($pid){
$status = 0;
//阻塞父进程,直到子进程结束,不适合需要长时间运行的脚本,可使用pcntl_wait($status, 0)实现非阻塞式
pcntl_wait($status);
// parent proc code
exit;
}else{
// child proc code
//结束当前子进程,以防止生成僵尸进程
if(function_exists("posix_kill")){
posix_kill(getmypid(), SIGTERM);
}else{
system('kill -9'. getmypid());
}
exit;
}
}
}else{
// 不支持多进程处理时的代码在这里
}
//.....
?>
如果不需要阻塞进程,而又想得到子进程的退出状态,则可以注释掉pcntl_wait($status)语句,或写成:
<?php
pcntl_wait($status, 1);
//或
pcntl_wait($status, WNOHANG);
pcntl_wait($status, 1);
//或
pcntl_wait($status, WNOHANG);
在上面的代码中,如果父进程退出(使用exit函数退出或redirect),则会导致子进程成为僵尸进程(会交给init进程控制),子进程不再执行。
僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程.(zombie)进程。任何进程在退出前(使用exit退出) 都会变成僵尸进程(用于保存进程的状态等信息),然后由init进程接管。如果不及时回收僵尸进程,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。
预防僵尸进程有以下几种方法:
1. 父进程通过wait和waitpid等函数使其等待子进程结束,然后再执行父进程中的代码,这会导致父进程挂起。上面的代码就是使用这种方式实现的,但在WEB环境下,它不适合子进程需要长时间运行的情况(会导致超时)。
使用wait和waitpid方法使父进程自动回收其僵尸子进程(根据子进程的返回状态),waitpid用于临控指定子进程,wait是对于所有子进程而言。
2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用wait回收
3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号,例如:
pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
//....code
4. 还有一个技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程再fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。下面是一个例子:
#include "apue.h"
#include <sys/wait.h>
int main(void){
pid_t pid;
if ((pid = fork()) < 0){
err_sys("fork error");
} else if (pid == 0){ /**//* first child */
if ((pid = fork()) < 0){
err_sys("fork error");
}elseif(pid > 0){
exit(0); /**//* parent from second fork == first child */
}
/**
* We're the second child; our parent becomes init as soon
* as our real parent calls exit() in the statement above.
* Here's where we'd continue executing, knowing that when
* we're done, init will reap our status.
*/
sleep(2);
printf("second child, parent pid = %d ", getppid());
exit(0);
}
if (waitpid(pid, NULL, 0) != pid) /**//* wait for first child */
err_sys("waitpid error");
/**
* We're the parent (the original process); we continue executing,
* knowing that we're not the parent of the second child.
*/
exit(0);
}
在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD信号处理函数调用 waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程,无法正常结束,此时即使是root身份kill-9也不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在),僵尸进程成为”孤儿进程”,过继给1号进程init,init会定期调用wait回收清理这些父进程已退出的僵尸子进程。
所以,上面的示例可以改成:
<?php
//.....
//需要安装pcntl的php扩展,并加载它
if(function_exists("pcntl_fork")){
//生成第一个子进程
$pid = pcntl_fork(); //$pid即所产生的子进程id
if($pid == -1){
//子进程fork失败
die('could not fork');
}else{
if($pid){
//父进程code
sleep(5); //等待5秒
exit(0); //或$this->_redirect('/');
}else{
//第一个子进程code
//产生孙进程
if(($gpid = pcntl_fork()) < 0){ ////$gpid即所产生的孙进程id
//孙进程产生失败
die('could not fork');
}elseif($gpid > 0){
//第一个子进程code,即孙进程的父进程
$status = 0;
$status = pcntl_wait($status); //阻塞子进程,并返回孙进程的退出状态,用于检查是否正常退出
if($status ! = 0) file_put_content('filename', '孙进程异常退出');
//得到父进程id
//$ppid = posix_getppid(); //如果$ppid为1则表示其父进程已变为init进程,原父进程已退出
//得到子进程id:posix_getpid()或getmypid()或是fork返回的变量$pid
//kill掉子进程
//posix_kill(getmypid(), SIGTERM);
exit(0);
}else{ //即$gpid == 0
//孙进程code
//....
//结束孙进程(即当前进程),以防止生成僵尸进程
if(function_exists('posix_kill')){
posix_kill(getmypid(), SIGTERM);
}else{
system('kill -9'. getmypid());
}
exit(0);
}
}
}
}else{
// 不支持多进程处理时的代码在这里
}
//.....
?>
//.....
//需要安装pcntl的php扩展,并加载它
if(function_exists("pcntl_fork")){
//生成第一个子进程
$pid = pcntl_fork(); //$pid即所产生的子进程id
if($pid == -1){
//子进程fork失败
die('could not fork');
}else{
if($pid){
//父进程code
sleep(5); //等待5秒
exit(0); //或$this->_redirect('/');
}else{
//第一个子进程code
//产生孙进程
if(($gpid = pcntl_fork()) < 0){ ////$gpid即所产生的孙进程id
//孙进程产生失败
die('could not fork');
}elseif($gpid > 0){
//第一个子进程code,即孙进程的父进程
$status = 0;
$status = pcntl_wait($status); //阻塞子进程,并返回孙进程的退出状态,用于检查是否正常退出
if($status ! = 0) file_put_content('filename', '孙进程异常退出');
//得到父进程id
//$ppid = posix_getppid(); //如果$ppid为1则表示其父进程已变为init进程,原父进程已退出
//得到子进程id:posix_getpid()或getmypid()或是fork返回的变量$pid
//kill掉子进程
//posix_kill(getmypid(), SIGTERM);
exit(0);
}else{ //即$gpid == 0
//孙进程code
//....
//结束孙进程(即当前进程),以防止生成僵尸进程
if(function_exists('posix_kill')){
posix_kill(getmypid(), SIGTERM);
}else{
system('kill -9'. getmypid());
}
exit(0);
}
}
}
}else{
// 不支持多进程处理时的代码在这里
}
//.....
?>
怎样产生僵尸进程的
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。
任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是”Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。
如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
另外,还可以写一个php文件,然后在以后台形式来运行它,例如:
<?php
//Action代码
public function createAction(){
//....
//将args替换成要传给insertLargeData.php的参数,参数间用空格间隔
system('php -f insertLargeData.php ' . ' args ' . '&');
$this->redirect('/');
}
?>
//Action代码
public function createAction(){
//....
//将args替换成要传给insertLargeData.php的参数,参数间用空格间隔
system('php -f insertLargeData.php ' . ' args ' . '&');
$this->redirect('/');
}
?>
然后在insertLargeData.php文件中做数据库操作。也可以用cronjob + php的方式实现大数据量的处理。
如果是在终端运行php命令,当终端关闭后,刚刚执行的命令也会被强制关闭,如果你想让其不受终端关闭的影响,可以使用nohup命令实现:
<?php
//Action代码
public function createAction(){
//....
//将args替换成要传给insertLargeData.php的参数,参数间用空格间隔
system('nohup php -f insertLargeData.php ' . ' args ' . '&');
$this->redirect('/');
}
?>
//Action代码
public function createAction(){
//....
//将args替换成要传给insertLargeData.php的参数,参数间用空格间隔
system('nohup php -f insertLargeData.php ' . ' args ' . '&');
$this->redirect('/');
}
?>
你还可以使用screen命令代替nohup命令。
10/08
24
最近比较PHP跟python, Erlang 的特性,发现PHP有很多人们不常用到的特性。用PHP CLI可以实现很多不错的应用。比如做爬虫, 长期运行的计算脚本, 完全可以取代其他语言来做 服务器的运维。这对于熟悉PHP的人来说如虎添翼。
为什么PHP多进程很好? 网游服务器大部分都使用多线程而不是多进程的原因也在于进程比线程更加稳定。而且多线程适合现在多核服务器的应用场景,更能发挥多核运算的能力。进程的维护可以用很多操作系统级别的工具。Message Queue解决了多大部分线程通信问题。所以PHP多进程很适合做服务器端的计算密集型的应用。
据一家越南IT公司介绍,他们成功的把PHP后台多进程用在法律文件的分发、处理银行账户的金额这样的企业级的应用上。
使用后台PHP 进程可以不影响服务器同时处理网页的请求。这种后台进程一旦发生失败很容易查处原因进行恢复或者补救,所以健壮性更高。不同的进程相互隔离,更加高效,可以统一调度各个服务进程。
PHP 是目前应用最广泛的WEB开发语言,所以用PHP来做服务器端的应用可以降低成本。可以用现有人员、现有配置、甚至做到代码重用。
什么样的场景更适合用PHP后台多进程呢?比如邮件的分发、远程服务的调用、数据的聚合、计划任务、计算结果的缓存这些不需要立即返回的地方。
PHP单进程在某些地方完全可以达到目的,而且更加容易实现,不用考虑进程的同步问题,不用考虑数据的共享问题。
PHP CLI (SAPI SERVER API)命令行接口可以用来做CRON计划任务, 图形界面程序 (使用GTK库)。
PHP CLI 例子:
php -f test.php
php -r “echo time();”
php -R as python style
PHP读取命令行参数:
#!/usr/bin/php -q
echo “Test Arguments:\n”;
echo $_SERVER["argc"].”\n”;
echo $_SERVER["argv"][0].”\n”;
?>
PHP命令行接口标准输入输出:
#!/usr/bin/php -q
/* Define STDIN in case if it is not already defined by PHP for some reason */
if(!defined(“STDIN”)) {
define(“STDIN”, fopen(‘php://stdin’,'r’))
}
echo “Hello! What is your name (enter below):\n”;
$strName = fread(STDIN, 80); // Read up to 80 characters or a newline
echo ‘Hello ‘ , $strName , “\n”;
?>
CRONJOB可以定时运行某些任务,但要防止重复运行。开始时创建一个锁文件, 结束时删除。或者用ps命令来处理。
任务队列可以用Mysql来实现,或者KEY/VALUE数据库,或者消息队列来实现。
进程控制相关函数:
Process Control Extensions
pcntl_fork()
posix_setsid()
posix_kill
pcntl_wait
pcntl_signal
o SIGHUP
o SIGTERM; system shutdown, kill
o SIGINT; sent by Ctrl+c
o SIGKILL (uncatchable); unresponsive, kill -9
o SIGCHLD; child status change
o SIGSTP; sent by Ctrl+z
o SIGCONT; resume from stop, fg
PHP不能对某些错误抛出异常,如何提高PHP多进程应用的容错性?
可以监控进程,依赖进程失败后报告
用CRONJOB实现监控进程
将被监控进程PID写成文件
定时检查PID文件是否存在 检查ps -o pid= 或者file_exists(‘/proc/’)
如果线程不存在 重启进程。
回顾以前用JAVA或者Python做的服务器端的服务都可以用PHP来实现。单一语言更容易维护。以往人们对于WEB语言的认识很片面,例如多线程、事 务这些东西都可以改变方式来达到同样的目的。
http://blog.eood.cn/server-side-php-progress-program-best-practice
为什么PHP多进程很好? 网游服务器大部分都使用多线程而不是多进程的原因也在于进程比线程更加稳定。而且多线程适合现在多核服务器的应用场景,更能发挥多核运算的能力。进程的维护可以用很多操作系统级别的工具。Message Queue解决了多大部分线程通信问题。所以PHP多进程很适合做服务器端的计算密集型的应用。
据一家越南IT公司介绍,他们成功的把PHP后台多进程用在法律文件的分发、处理银行账户的金额这样的企业级的应用上。
使用后台PHP 进程可以不影响服务器同时处理网页的请求。这种后台进程一旦发生失败很容易查处原因进行恢复或者补救,所以健壮性更高。不同的进程相互隔离,更加高效,可以统一调度各个服务进程。
PHP 是目前应用最广泛的WEB开发语言,所以用PHP来做服务器端的应用可以降低成本。可以用现有人员、现有配置、甚至做到代码重用。
什么样的场景更适合用PHP后台多进程呢?比如邮件的分发、远程服务的调用、数据的聚合、计划任务、计算结果的缓存这些不需要立即返回的地方。
PHP单进程在某些地方完全可以达到目的,而且更加容易实现,不用考虑进程的同步问题,不用考虑数据的共享问题。
PHP CLI (SAPI SERVER API)命令行接口可以用来做CRON计划任务, 图形界面程序 (使用GTK库)。
PHP CLI 例子:
php -f test.php
php -r “echo time();”
php -R as python style
PHP读取命令行参数:
#!/usr/bin/php -q
echo “Test Arguments:\n”;
echo $_SERVER["argc"].”\n”;
echo $_SERVER["argv"][0].”\n”;
?>
PHP命令行接口标准输入输出:
#!/usr/bin/php -q
/* Define STDIN in case if it is not already defined by PHP for some reason */
if(!defined(“STDIN”)) {
define(“STDIN”, fopen(‘php://stdin’,'r’))
}
echo “Hello! What is your name (enter below):\n”;
$strName = fread(STDIN, 80); // Read up to 80 characters or a newline
echo ‘Hello ‘ , $strName , “\n”;
?>
CRONJOB可以定时运行某些任务,但要防止重复运行。开始时创建一个锁文件, 结束时删除。或者用ps命令来处理。
任务队列可以用Mysql来实现,或者KEY/VALUE数据库,或者消息队列来实现。
进程控制相关函数:
Process Control Extensions
pcntl_fork()
posix_setsid()
posix_kill
pcntl_wait
pcntl_signal
o SIGHUP
o SIGTERM; system shutdown, kill
o SIGINT; sent by Ctrl+c
o SIGKILL (uncatchable); unresponsive, kill -9
o SIGCHLD; child status change
o SIGSTP; sent by Ctrl+z
o SIGCONT; resume from stop, fg
PHP不能对某些错误抛出异常,如何提高PHP多进程应用的容错性?
可以监控进程,依赖进程失败后报告
用CRONJOB实现监控进程
将被监控进程PID写成文件
定时检查PID文件是否存在 检查ps -o pid=
如果线程不存在 重启进程。
回顾以前用JAVA或者Python做的服务器端的服务都可以用PHP来实现。单一语言更容易维护。以往人们对于WEB语言的认识很片面,例如多线程、事 务这些东西都可以改变方式来达到同样的目的。
http://blog.eood.cn/server-side-php-progress-program-best-practice
10/08
24
将PHP程序作为Linux守护进程的方法:
nohup /usr/local/php/bin/php /opt/zhangyan.php 2>&1 > /dev/null &
(nohup命令可以在用户退出终端后仍然执行程序,“2>&1 > /dev/null”表示不显示标准输出和错误输出,最后的&表示推到后台执行。)
http://blog.s135.com/post/311/
nohup /usr/local/php/bin/php /opt/zhangyan.php 2>&1 > /dev/null &
(nohup命令可以在用户退出终端后仍然执行程序,“2>&1 > /dev/null”表示不显示标准输出和错误输出,最后的&表示推到后台执行。)
http://blog.s135.com/post/311/
10/06
23
#!/bin/sh
for F in `ls user_post/config.*.php`
do
/php/bin/php-cgi $F > /dev/null &
done
for F in `ls user_post/config.*.php`
do
/php/bin/php-cgi $F > /dev/null &
done
放到后台运行的方法
nohup jasonyu.php &
10/06
14
memcache模块是一个高效的守护进程,提供用于内存缓存的过程式程序和面向对象的方便的接口,特别是对于设计动态web程序时减少对数据库的访问。.....
10/06
6
说在前面:
1、以下题目,除了编程任务外其他都需要写在给你提供的草纸上。纸张是珍贵的地球资源,请节约使用。编程任务在有相应的环境时,会要求上机书写,实在没有条件,就只能写在草纸上了。
2、时间:
基础任务+进阶任务+设计任务 = 90分钟
编程任务 = 60分钟
基础任务:
1、请列举你能想到的UNIX信号,并说明信号用途。
2、请列举、你能想到的所有的字符串查找算法,并加注释简单说明。
3、有一个IP地址(192.168.0.1),请写出其32位无符号整数形式。
4、写出、你能想到的所有HTTP返回状态值,并说明用途(比如:返回404表示找不到页面)
基础任务-选作(会得到额外分数):
1、画几个你最熟悉的SERVER端模型出来(格式不重要,尽量将图画清楚,说明思路即可)
进阶任务:
1、PHP的垃圾收集机制是怎样的?
说明:
1)如果,你熟悉PHP源码,那么请从源码入手,回答些问题,会获得额外加分
2)如果,你不熟悉PHP源码,那么尽你所能,多写点东西,包括利用自己的编程直觉得到的信息,都可以。
3)对,则有分,错误不扣,不写无分。
2、请写出HTTP头,并符合以下要求:
1)这是一个post请求
2)目标:http://www.example.com:8080/test
3)POST变量:
username: test
pwd: test2
intro: Hello world!
4)包含以下COOKIE信息:
cur_query: you&me
说明:
1)如果,你记不得某个HTTP协议中的指令字了,那么,无奈这举是用“汉字”代替。
2)如果,你能记住更多的HTTP协议指令字,那么多写几句,总是没坏处,对吧?
3)最关键的,只需要画出正确的“轮廓”(还记得httpwatch等工具打印出来的头部吗?那就是“轮廓”的含义),也会有分数,但如果,连“轮廓”都写错了,那么就很遗憾了。
设计任务:
1、最近总有人骚扰我们的投票模块,需要你来设计一个投票限制的东东
要求如下:
1)要求每个QQ号码(假设此QQ号码在UNIT32内可以表示)10分钟这内只能投5票。
2)我们的用户很踊跃,平均每天要有2000万人左右通过此程序投票。
说明:
1)无需写代码,只需要图跟文字即可。
2)对于关键逻辑,请用图加代码表示出来,这也是对你文字表达能力的一个考验。
3)对你能想到的所有的边界条件列出来,这是对你逻辑思维全面与敏捷性的考验。
4)存储部分,尽你所能吧。如果,你需要一个自己设计的存储层,那么把这个存储层的实现,用文字+图片方式描述清楚,要是设计合理,你会获得华丽的奖分。
编程任务:
1、我们碰到了大麻烦,一个新来的传教士惹恼了上帝,上帝很愤怒,要求我们把圣经(bbe.txt)背熟,直至他说哪个单词,我们就要飞快的回答出这个单词在第几行第几个单词位置。听说你是个优秀的程序员,那么髟助我们完成这个不可能的任务吧。
要求如下:
1)/myworks/example/bbe.txt,98版本英文圣经一本
2)输入部分要求如下:php ./example.php [单词]
3)输出部分如下:[单词] 1,2 2,4 5,6 表示:此单词在1行2列(第二个单词),2行4列...
说明:
1)此文本4MB之巨...
2)单词的含义:由英文字母(大小写),数字(0-9)组成的串
3)提供给你的机器OS为ubuntu 9.10,内存只有1G,而且,很不幸的,其中700M用来做了别的
4)上机考试不允许上网,但我装了man文档以及读取CHM以及PDF的阅读器,在电脑的桌面的CHM文件夹中,有相应的PHP参考手册
5)算法复杂度要求不能大于O(N^2)(就是N的平方)
6)什么?PHP低效且用起来不顺手,好的,你可以用别的语言来实现。但注意:提供给你的机器上只有python 2.4/perl 5.8/gcc[g++] 4.1
1、以下题目,除了编程任务外其他都需要写在给你提供的草纸上。纸张是珍贵的地球资源,请节约使用。编程任务在有相应的环境时,会要求上机书写,实在没有条件,就只能写在草纸上了。
2、时间:
基础任务+进阶任务+设计任务 = 90分钟
编程任务 = 60分钟
基础任务:
1、请列举你能想到的UNIX信号,并说明信号用途。
2、请列举、你能想到的所有的字符串查找算法,并加注释简单说明。
3、有一个IP地址(192.168.0.1),请写出其32位无符号整数形式。
4、写出、你能想到的所有HTTP返回状态值,并说明用途(比如:返回404表示找不到页面)
基础任务-选作(会得到额外分数):
1、画几个你最熟悉的SERVER端模型出来(格式不重要,尽量将图画清楚,说明思路即可)
进阶任务:
1、PHP的垃圾收集机制是怎样的?
说明:
1)如果,你熟悉PHP源码,那么请从源码入手,回答些问题,会获得额外加分
2)如果,你不熟悉PHP源码,那么尽你所能,多写点东西,包括利用自己的编程直觉得到的信息,都可以。
3)对,则有分,错误不扣,不写无分。
2、请写出HTTP头,并符合以下要求:
1)这是一个post请求
2)目标:http://www.example.com:8080/test
3)POST变量:
username: test
pwd: test2
intro: Hello world!
4)包含以下COOKIE信息:
cur_query: you&me
说明:
1)如果,你记不得某个HTTP协议中的指令字了,那么,无奈这举是用“汉字”代替。
2)如果,你能记住更多的HTTP协议指令字,那么多写几句,总是没坏处,对吧?
3)最关键的,只需要画出正确的“轮廓”(还记得httpwatch等工具打印出来的头部吗?那就是“轮廓”的含义),也会有分数,但如果,连“轮廓”都写错了,那么就很遗憾了。
设计任务:
1、最近总有人骚扰我们的投票模块,需要你来设计一个投票限制的东东
要求如下:
1)要求每个QQ号码(假设此QQ号码在UNIT32内可以表示)10分钟这内只能投5票。
2)我们的用户很踊跃,平均每天要有2000万人左右通过此程序投票。
说明:
1)无需写代码,只需要图跟文字即可。
2)对于关键逻辑,请用图加代码表示出来,这也是对你文字表达能力的一个考验。
3)对你能想到的所有的边界条件列出来,这是对你逻辑思维全面与敏捷性的考验。
4)存储部分,尽你所能吧。如果,你需要一个自己设计的存储层,那么把这个存储层的实现,用文字+图片方式描述清楚,要是设计合理,你会获得华丽的奖分。
编程任务:
1、我们碰到了大麻烦,一个新来的传教士惹恼了上帝,上帝很愤怒,要求我们把圣经(bbe.txt)背熟,直至他说哪个单词,我们就要飞快的回答出这个单词在第几行第几个单词位置。听说你是个优秀的程序员,那么髟助我们完成这个不可能的任务吧。
要求如下:
1)/myworks/example/bbe.txt,98版本英文圣经一本
2)输入部分要求如下:php ./example.php [单词]
3)输出部分如下:[单词] 1,2 2,4 5,6 表示:此单词在1行2列(第二个单词),2行4列...
说明:
1)此文本4MB之巨...
2)单词的含义:由英文字母(大小写),数字(0-9)组成的串
3)提供给你的机器OS为ubuntu 9.10,内存只有1G,而且,很不幸的,其中700M用来做了别的
4)上机考试不允许上网,但我装了man文档以及读取CHM以及PDF的阅读器,在电脑的桌面的CHM文件夹中,有相应的PHP参考手册
5)算法复杂度要求不能大于O(N^2)(就是N的平方)
6)什么?PHP低效且用起来不顺手,好的,你可以用别的语言来实现。但注意:提供给你的机器上只有python 2.4/perl 5.8/gcc[g++] 4.1
10/05
28
使用脚本语言最大的好处之一就是可利用其拥有的自动垃圾回收机制(释放内存)。你不需要在使用完变量后做任何释放内存的处理,PHP会帮你完成。
当然,我们可以按自己的意愿调用 unset() 函数来释放内存,但通常不需要这么做。
不过在PHP里,至少有一种情况内存不会得到自动释放,即便是手动调用 unset()。详情可考:http://bugs.php.net/bug.php?id=33595。
问题症状
当然,我们可以按自己的意愿调用 unset() 函数来释放内存,但通常不需要这么做。
不过在PHP里,至少有一种情况内存不会得到自动释放,即便是手动调用 unset()。详情可考:http://bugs.php.net/bug.php?id=33595。
问题症状





