RSSI(接收信号强度)Received Signal Strength Indicator

RSSI是负值并不代表其信号值就是负值。
这个值单位是dbm,是根据公式 N dbm=10log(W/1mw)得来的,由公司可知,当功率值W小于1mw时,N就是负值。所以RSSI是负值说明其功率小于1mw。

1.dBm

dBm 用于表达功率的绝对值,计算公式为

10lg(P功率值/lmw)

[例]如果发射功率P为10w 则按dBm单位进行折算后的值应为10lg(10w/1mw)=10lg(10000)=40dBm      

RSSI(接收信号强度)Received Signal Strength Indicator
为保证通信质量和越区切换,实现动态频率选择或系统功率控制功能。基站一般要求移动台报告所接收到信号强度,以便系统作出正确的选择和决定。因此移动 台所报告的接收信号强度是否准确,直接关系到整个通信系统的性能。
RSSI(接收信号强度)的校准与功率电平校准非常类似,它一般也分为两大类校准,一类为RSSI(接收信号强度)精度的校准,另一类就是由于移动台 对不同频率的输入信号的响应不同,所引起的RSSI(接收信号强度)误差的校准,因此很多厂家也把这种校准称之为RSSI信道补偿校准。
由于目前手机,尤其是GSM手机,RSSI(接收信号强度)的测量各个厂家都有各自的设计,因此RSSI(接收信号强度)精度校准也是各有各的方法, 但大体原理是一致的,那就是为了保证在不同大小的接收信号下,在解调前信号大小是一致的,电路中都会有个AGC电路,AGC工作时的放大倍数与 RSSI(接收信号强度)成线性比例关系;因此校准AGC的放大倍数,保证AGC的输出结果,就是在对RSSI(接收信号强度)精度校准,目前大多数厂家 都把RSSI(接收信号强度)精度的校准说成是AGC校准,本文以后也延续这个叫法。AGC校准的方法一般是施加一个已知大小的信号给移动台,由移动台报 告接收信号强度(RSSI),移动台报告的接收信号强度(RSSI)与真实值的误差,就是AGC的误差,从这点也可以看出,AGC的校准是要通过移动台 RSSI的报告来完成的。AGC校准的原理虽然大体相似,但不同厂家,其AGC电路的设计是不同的,故AGC校准要校准的具体内容,校准的具体方式和方法 是有很大差别的。
正如前面所述,RSSI信道补偿校准是为了克服由于频率响应所造成的误差,因此不论是哪个厂家,RSSI校准方案大体都是相同的:在不同的信道上,在 相同的外部输入信号下,移动台报告的RSSI(接收信号强度)的误差,作为各个信道的RSSI(接收信号强度)的补偿值。值得注意的是,一般厂家都要求, 在作RSSI校准前,都要先做AGC的校准,关于这个要求也是容易理解的。

在CDMA网络中,RSSI的范围在-110dbm — -20dbm之间。一般来说,如果RSSI<-95dbm,说明当前网络信号覆盖很差,几乎没什么信 号;-95dmb<RSSI<-90dbm,说明当前网络信号覆盖很弱;RSSI〉-90dbm,说明当前网络信号覆盖较好。所以,一般都是 以-90dbm为临界点,来初略判断当前网络覆盖水平。
所以,RSSI 与 Rx反映的是当前区域的网络信号覆盖强弱水平,Tx反映的是当前区域的反向链路质量和上行干扰水平。

求解三次方程.cpp

  1. #include <iostream.h>     
  2. #include <math.h>     
  3. void init(double &a,double &b,double &c,double &d,double &x)     
  4. {     
  5.     do     
  6.     {     
  7.         cout<<“请输入一元三次方程ax^3+bx^2+cx+d=0的各项系数a,b,c,d:”;     
  8.         cin>>a>>b>>c>>d;     
  9.         if (a==0) cout<<“三次项系数不能为0!\n”;     
  10.     } while (a==0);     
  11.     cout<<“您要求解的方程为:”<<a<<“x^3+”<<b<<“x^2+”<<c<<“x+”<<d<<“=0\n”;     
  12.     if (a<0)     
  13.     {     
  14.         a=-a;     
  15.         b=-b;     
  16.         c=-c;     
  17.         d=-d;     
  18.     }     
  19.     cout<<“请输入一个初始值,将在其附近寻根:”;     
  20.     cin>>x;     
  21. }     
  22. double calc(double a,double b,double c,double d,double x)     
  23. {     
  24.     return (a*x*x*x+b*x*x+c*x+d);     
  25. }     
  26. void waya(double a,double b,double c,double d,double x)     
  27. {     
  28.     double max,min;     
  29.     double t;     
  30.     t=calc(a,b,c,d,x);     
  31.     max=x;min=x;     
  32.     if (t<0)      
  33.     {     
  34.         do {     
  35.             max++;     
  36.         } while (calc(a,b,c,d,max)<0);     
  37.         if (calc(a,b,c,d,max)==0)     
  38.         {     
  39.             x=max;     
  40.             t=0;     
  41.         }     
  42.     }     
  43.     if (t>0)      
  44.     {     
  45.         do {     
  46.             min–;     
  47.         } while (calc(a,b,c,d,min)>0);     
  48.         if (calc(a,b,c,d,min)==0)     
  49.         {     
  50.             x=min;     
  51.             t=0;     
  52.         }     
  53.     }     
  54.     while (t!=0&&max-min>1e-10)     
  55.     {     
  56.         if (calc(a,b,c,d,max)*t<0) min=x;     
  57.         else max=x;     
  58.         x=(max+min)/2;     
  59.         t=calc(a,b,c,d,x);     
  60.     }     
  61.     cout<<“原方程的根为:x=”<<x;     
  62. }     
  63. void wayb(double a,double b,double c,double d,double x)     
  64. {     
  65.     double max,min,pre;     
  66.     double t;     
  67.         t=calc(a,b,c,d,x);     
  68.     max=x;min=x;pre=1e10;     
  69.     if (t<0)      
  70.     {     
  71.         do {     
  72.             max++;     
  73.         } while (calc(a,b,c,d,max)<0);     
  74.         if (calc(a,b,c,d,max)==0)     
  75.         {     
  76.             x=max;     
  77.             t=0;     
  78.         }     
  79.     }     
  80.     if (t>0)      
  81.     {     
  82.         do {     
  83.             min–;     
  84.         } while (calc(a,b,c,d,min)>0);     
  85.         if (calc(a,b,c,d,min)==0)     
  86.         {     
  87.             x=min;     
  88.             t=0;     
  89.         }     
  90.     }     
  91.     while (t!=0&&fabs(pre-x)>1e-10)     
  92.     {     
  93.         pre=x;     
  94.         if (calc(a,b,c,d,max)*t<0) min=x;     
  95.         else max=x;     
  96.         x=min-(max-min)/(calc(a,b,c,d,max)-calc(a,b,c,d,min))*calc(a,b,c,d,min);     
  97.         t=calc(a,b,c,d,x);     
  98.     }     
  99.     cout<<“原方程的根为:x=”<<x;     
  100. }     
  101. void wayc(double a,double b,double c,double d,double x)     
  102. {     
  103.     while (calc(0,a*3,b*2,c,x)==0) x++;     
  104.     double px,dx;     
  105.     px=x-calc(a,b,c,d,x)/calc(0,a*3,b*2,c,x);     
  106.     dx=px>x?px-x:x-px;     
  107.     while (dx>1e-10)     
  108.     {     
  109.         x=px;     
  110.         while (calc(0,a*3,b*2,c,x)==0) x++;     
  111.         px=x-calc(a,b,c,d,x)/calc(0,a*3,b*2,c,x);     
  112.         dx=px>x?px-x:x-px;     
  113.     }     
  114.     cout<<“原方程的根为:x=”<<x;     
  115. }     
  116. void menus()     
  117. {     
  118.     int choice;     
  119.     double a,b,c,d,x;     
  120.     init(a,b,c,d,x);     
  121.     do     
  122.     {     
  123.         cout<<“方法一:二分逼近\n方法二:弦截法\n方法三:牛顿迭代法\n”;     
  124.         cout<<“请选择一种方法:(1——3)”;     
  125.         cin>>choice;     
  126.     } while (choice<1||choice>3);     
  127.     switch (choice)     
  128.     {     
  129.     case 1:waya(a,b,c,d,x);break;     
  130.     case 2:wayb(a,b,c,d,x);break;     
  131.     case 3:wayc(a,b,c,d,x);break;     
  132.     }     
  133. }     
  134. void main()     
  135. {     
  136.     char conti;     
  137.     cout<<“*用多种方法求解三次方程\n*”;     
  138.     do     
  139.     {     
  140.         menus();     
  141.         cout<<“继续吗?(y/n)”;     
  142.         cin>>conti;     
  143.     } while (conti!=‘n’&&conti!=‘N’);     
  144. }       
 
 
 
 
 

Ajax请求跨域的理解(端口,域名,主机)

今天同事遇到个奇怪的问题,就是他用JQ的AJAX请求一个URL的时候,通过FIREBUG看到的返回状态时200,但是没有数据,而且那个请求时红色 的,就像返回回来的是404一样。我帮他调了很久,最后看到他浏览器的访问地址是www.xxx.com/xxx,而他AJAX请求的是 www.xxx.com:8080/xxx,端口不一致。我突然想到肯定是AJAX跨域,不过这个可是一个服务器,只是不同的端口而已。然后让他吧 8080去掉,就可以了。

 
经过几次测试,发现浏览器对AJAX跨域限制很严格,我有一个主机,IP为A,A上80端口,81端口都有服务,然后IP为A的主机上绑定了两个域 名,www.test.com 和www.test1.com,通过这两个IP都能方位到80的服务,但是如果在www.test.com上用AJAX请求www.test1.com的 URL,就不行。就属于跨域。同样,www.test.com上访问www.test.com:81的请求也是跨域。虽然是同一个主机,同一个IP,但是 只要域名不一致,甚至端口不一致,都属于跨域。
 
 
那么,如果一个主机上不同端口上部署有不同的服务,怎么用AJAX去调用呢?我的解决办法是通过中间层转发请求。比如www.test.com上要用 AJAX请求81的服务,就在80上写一个功能,请求80,把要传递的参数传给80,在由80的服务器端去发请求,将请求到的数据返回给AJAX.
 

Ubuntu防火墙ufw

ubuntu 9.10默认的是UFW防火墙,已经支持界面操作了。在命令行运行ufw命令就可以看到提示的一系列可进行的操作。

最简单的一个操作:

sudo ufw status可检查防火墙的状态,我的返回的是:不活动
sudo ufw version防火墙版本:ufw 0.29-4ubuntu1
Copyright 2008-2009 Canonical Ltd.

ubuntu 系统默认已安装ufw.

(以下内容引自:http://www.jmhdtv.com/post/198.html)
1.安装
sudo apt-get install ufw

2.启用
sudo ufw enable
sudo ufw default deny

运行以上两条命令后,开启了防火墙,并在系统启动时自动开启。关闭所有外部对本机的访问,但本机访问外部正常。

3.开启/禁用
sudo ufw allow|deny [service]

打开或关闭某个端口,例如:
sudo ufw allow smtp 允许所有的外部IP访问本机的25/tcp (smtp)端口
sudo ufw allow 22/tcp 允许所有的外部IP访问本机的22/tcp (ssh)端口
sudo ufw allow 53 允许外部访问53端口(tcp/udp)
sudo ufw allow from 192.168.1.100 允许此IP访问所有的本机端口
sudo ufw allow proto udp 192.168.0.1 port 53 to 192.168.0.2 port 53
sudo ufw deny smtp 禁止外部访问smtp服务
sudo ufw delete allow smtp 删除上面建立的某条规则

4.查看防火墙状态
sudo ufw status

一般用户,只需如下设置:
sudo apt-get install ufw
sudo ufw enable
sudo ufw default deny

以上三条命令已经足够安全了,如果你需要开放某些服务,再使用sudo ufw allow开启。

开启/关闭防火墙 (默认设置是’disable’)
sudo ufw enable|disable

转换日志状态
sudo ufw logging on|off

设置默认策略 (比如 “mostly open” vs “mostly closed”)

sudo ufw default allow|deny

许 可或者屏蔽端口 (可以在“status” 中查看到服务列表)。可以用“协议:端口”的方式指定一个存在于/etc/services中的服务名称,也可以通过包的meta-data。 ‘allow’ 参数将把条目加入 /etc/ufw/maps ,而 ‘deny’ 则相反。基本语法如下:
sudo ufw allow|deny [service]

显示防火墙和端口的侦听状态,参见 /var/lib/ufw/maps。括号中的数字将不会被显示出来。
sudo ufw status

UFW 使用范例:

允许 53 端口
$ sudo ufw allow 53

禁用 53 端口
$ sudo ufw delete allow 53

允许 80 端口
$ sudo ufw allow 80/tcp

禁用 80 端口
$ sudo ufw delete allow 80/tcp

允许 smtp 端口
$ sudo ufw allow smtp

删除 smtp 端口的许可
$ sudo ufw delete allow smtp

允许某特定 IP
$ sudo ufw allow from 192.168.254.254

删除上面的规则
$ sudo ufw delete allow from 192.168.254.254

linux 2.4内核以后提供了一个非常优秀的防火墙工具:netfilter/iptables,他免费且功能强大,可以对流入、流出的信息进行细化控制,它可以 实现防火墙、NAT(网络地址翻译)和数据包的分割等功能。netfilter工作在内核内部,而iptables则是让用户定义规则集的表结构。

但是iptables的规则稍微有些“复杂”,因此ubuntu提供了ufw这个设定工具,以简化iptables的某些设定,其后台仍然是iptables。ufw 即uncomplicated firewall的简称,一些复杂的设定还是要去iptables。

ufw相关的文件和文件夹有:

/etc /ufw/:里面是一些ufw的环境设定文件,如 before.rules、after.rules、sysctl.conf、ufw.conf,及 for ip6 的 before6.rule 及 after6.rules。这些文件一般按照默认的设置进行就ok。

若开启ufw之 后,/etc/ufw/sysctl.conf会覆盖默认的/etc/sysctl.conf文件,若你原来的/etc/sysctl.conf做了修 改,启动ufw后,若/etc/ufw/sysctl.conf中有新赋值,则会覆盖/etc/sysctl.conf的,否则还以/etc /sysctl.conf为准。当然你可以通过修改/etc/default/ufw中的“IPT_SYSCTL=”条目来设置使用哪个 sysctrl.conf.

/var/lib/ufw/user.rules 这个文件中是我们设置的一些防火墙规则,打开大概就能看明白,有时我们可以直接修改这个文件,不用使用命令来设定。修改后记得ufw reload重启ufw使得新规则生效。

下面是ufw命令行的一些示例:

ufw enable/disable:打开/关闭ufw
ufw status:查看已经定义的ufw规则
ufw default allow/deny:外来访问默认允许/拒绝
ufw allow/deny 20:允许/拒绝 访问20端口,20后可跟/tcp或/udp,表示tcp或udp封包。
ufw allow/deny servicename:ufw从/etc/services中找到对应service的端口,进行过滤。
ufw allow proto tcp from 10.0.1.0/10 to 本机ip port 25:允许自10.0.1.0/10的tcp封包访问本机的25端口。
ufw delete allow/deny 20:删除以前定义的”允许/拒绝访问20端口”的规则

UFW防火墙是一个主机端的iptables类防火墙配置工具。这个工具的目的是提供给用户一个可以轻松驾驭的界面,就像包集成和动态检测开放的端口一样。

在Ubuntu中安装UFW:

目前这个包存在于Ubuntu 8.04的库中。

sudo apt-get install ufw

上面这行命令将把软件安装到您系统中。

开启/关闭防火墙 (默认设置是’disable’)

# ufw enable|disable

转换日志状态

# ufw logging on|off

设置默认策略 (比如 “mostly open” vs “mostly closed”)

# ufw default allow|deny

许 可或者屏蔽某些入埠的包 (可以在“status” 中查看到服务列表[见后文])。可以用“协议:端口”的方式指定一个存在于/etc/services中的服务名称,也可以通过包的meta-data。 ‘allow’ 参数将把条目加入 /etc/ufw/maps ,而 ‘deny’ 则相反。基本语法如下:

# ufw allow|deny [service]

显示防火墙和端口的侦听状态,参见 /var/lib/ufw/maps。括号中的数字将不会被显示出来。

# ufw status

[注意:上文中虽然没有使用 sudo,但是命令提示符号都是“#”。所以……你知道啥意思了哈。原文如此。──译者注]

UFW 使用范例:

允许 53 端口

$ sudo ufw allow 53

禁用 53 端口

$ sudo ufw delete allow 53

允许 80 端口

$ sudo ufw allow 80/tcp

禁用 80 端口

$ sudo ufw delete allow 80/tcp

允许 smtp 端口

$ sudo ufw allow smtp

删除 smtp 端口的许可

$ sudo ufw delete allow smtp

允许某特定 IP

$ sudo ufw allow from 192.168.254.254

删除上面的规则

$ sudo ufw delete allow from 192.168.254.254

关于MySQL的Float和Decimal精确度比较

float,double容易产生误差,对精确度要求比较高时,建议使用decimal来存,decimal在mysql内存是以字符串存储的,用于定义货币要求精确度高的数据。在数据迁移中,float(M,D)是非标准定义,最好不要这样使用。M为精度,D为标度。

mysql> create table t1(c1 float(10,2), c3 decimal(10,2)); 
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t1 values(1234567.23, 1234567.23);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t1;
+————+————+
| c1         | c3         |
+————+————+
| 1234567.25 | 1234567.23 | 
+————+————+
1 row in set (0.02 sec)

mysql> insert into t1 values(9876543.21, 9876543.12);                       
Query OK, 1 row affected (0.00 sec)

mysql> 
mysql> select * from t1;                             
+————+————+
| c1         | c3         |
+————+————+
| 1234567.25 | 1234567.23 | 
| 9876543.00 | 9876543.12 | 
+————+————+
2 rows in set (0.00 sec)

不定义fload, double的精度和标度时,存储按给出的数值存储,这于OS和当前的硬件有关。

decimal默认为decimal(10,0)

因为误差问题,在程序中,少用浮点数做=比较,可以做range比较。如果数值比较,最好使用decimal类型。

精度中,符号不算在内:

mysql> insert into t1 values(-98765430.21, -98765430.12);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t1;                                                              
+————–+————–+
| c1           | c3           |
+————–+————–+
|   1234567.25 |   1234567.23 | 
|   9876543.00 |   9876543.12 | 
| -98765432.00 | -98765430.12 | 
+————–+————–+
3 rows in set (0.00 sec)

float占4个字节,double占8个字节,decimail(M,D)占M+2个字节。

PHP CLI模式下的多进程应用

PHP在很多时候不适合做常驻的SHELL进程, 他没有专门的gc例程, 也没有有效的内存管理途径. 所以如果用PHP做常驻SHELL, 你会经常被内存耗尽导致abort而unhappy.

而且, 如果输入数据非法, 而脚本没有检测, 导致abort, 也会让你很不开心.

那? 怎么办呢?

多进程….

为什么呢?

优点:

1、使用多进程,子进程结束以后,内核会负责回收资源;

2、使用多进程,子进程异常退出不会导致整个进程Thread退出,父进程还有机会重建流程;

3、一个常驻主进程,只负责任务分发,逻辑更清楚;

Continue reading “PHP CLI模式下的多进程应用”

PHP多进程编程

使用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(;;) {  
          }  
      
      ?>


   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’;    //不会执行到该行  
      ?>

   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
      }

   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);  
      }  
      ?>

      子进程在输出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;
    }
  }
}
?>

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_wait($status)语句,或写成:

<?php
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{
         // 不支持多进程处理时的代码在这里
       }
       //…..
      ?>

怎样产生僵尸进程的
一 个进程在调用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(‘/’);
  }
?>

然后在insertLargeData.php文件中做数据库操作。也可以用cronjob + php的方式实现大数据量的处理。

如果是在终端运行php命令,当终端关闭后,刚刚执行的命令也会被强制关闭,如果你想让其不受终端关闭的影响,可以使用nohup命令实现:

<?php
  //Action代码
  public function createAction(){
    //….
    //将args替换成要传给insertLargeData.php的参数,参数间用空格间隔
    system(‘nohup php -f insertLargeData.php ‘ . ‘ args ‘ . ‘&’);
    $this->redirect(‘/’);
  }
?>

你还可以使用screen命令代替nohup命令。

使用fastcgi_finish_request提高页面响应速度

当PHP运行在FastCGI模式时,PHP FPM提供了一个名为fastcgi_finish_request的方法.按照文档上的说法,此方法可以提高请求的处理速度,如果有些处理可以在页面生成完后再进行,就可以使用这个方法.

听起来可能有些茫然,我们通过几个例子来说明一下:

通过浏览器访问此脚本, 结果发现并没有输出相应的字符串,但却生成了相应的文件.由此说明在调用fastcgi_finish_request后,客户端响应就已经结束,但与此同时服务端脚本却继续运行!

合理利用这个特性可以大大提升用户体验,趁热打铁再来一个例子:

代码里用sleep模拟一些耗时的操作,浏览时没有被堵塞,程序却都执行了,具体看日志.

末 了给您提个醒,Yahoo在Best Practices for Speeding Up Your Web Site中提到了Flush the Buffer Early,也就是利用PHP中的flush方法把内容尽快发到客户端去,它和本文介绍的fastcgi_finish_request有些许的类似.

转载附言: 我看了下这个方法, 在调用的时候, 会发送响应, 关闭连接. 但是不会结束PHP的运行. 相比调用flush, 或者我之前介绍的加速你的Echo来说, 这个方法能更加干脆一些.

另外, 从代码的可移植性讲的话, 可以在代码中附上如下代码:

不会造成代码部署在非fpm环境下造成问题.