PHP不支持多线程编程,于是,当需要同时执行多个任务的时候,就有些力不从心了;对于有些任务可以通过异步的方式来处理,如: 同时执行多个http请求;对于有些计算型的需求,或者是异步实现很麻烦的时候就不好办了。
当我再一次看fork/exec/system/source等类似的命令或系统调用的时候,觉得他们和之间的异同比较有意思,比如:
fork: 创建子进程,当前进程继续运行
exec: 当前进程直接让位于要执行的程序,用不返回
system: 使用当前进程的环境,创建一个子进程,等待子进程执行完成后返回; system=fork+exec+waitpid
于是想到了PHP中的popen,该方法可以和执行一个外部命令,但是不阻塞当前进程,只需要使用循环去收集执行结果就行了。
对于web程序,通过异步的http请求将任务提交给本机的web server或许比popen更加合算一些。
当我再一次看fork/exec/system/source等类似的命令或系统调用的时候,觉得他们和之间的异同比较有意思,比如:
fork: 创建子进程,当前进程继续运行
exec: 当前进程直接让位于要执行的程序,用不返回
system: 使用当前进程的环境,创建一个子进程,等待子进程执行完成后返回; system=fork+exec+waitpid
于是想到了PHP中的popen,该方法可以和执行一个外部命令,但是不阻塞当前进程,只需要使用循环去收集执行结果就行了。
对于web程序,通过异步的http请求将任务提交给本机的web server或许比popen更加合算一些。
首先,我编译PHP时使用如下命令:
./configure --prefix=/usr/local/php-5.3.3 && make
完成之后,发现默认的php.ini 为: /usr/local/php-5.3.3/lib/php.ini ; 于是我改变了注意,想修改prefix为 /usr/local , 如果不执行make clean的话,则
./configure --prefix=/usr/local && make
发现默认的php.ini 仍为: /usr/local/php-5.3.3/lib/php.ini
看来必须make clean了, 似乎configure之后生成的头文件: ./main/build-defs.h 是不被依赖的
./configure --prefix=/usr/local/php-5.3.3 && make
完成之后,发现默认的php.ini 为: /usr/local/php-5.3.3/lib/php.ini ; 于是我改变了注意,想修改prefix为 /usr/local , 如果不执行make clean的话,则
./configure --prefix=/usr/local && make
发现默认的php.ini 仍为: /usr/local/php-5.3.3/lib/php.ini
看来必须make clean了, 似乎configure之后生成的头文件: ./main/build-defs.h 是不被依赖的
测试脚本a.php:
- <?php
- include_once("b.php");
- echo "=====\n";
- include_once("b.php");
- echo "=====\n";
- include_once("c.php");
目录结构:
a.php
b.php
include_path 的 = .;/usr/local/lib/php
strace -e file php a.php
------------------------------
open("a.php", O_RDONLY) = 3 // 打开a.php
getcwd("/usr/home/junjie2", 4096) = 18
lstat64("/usr/home/junjie2/a.php", {st_mode=S_IFREG|0644, st_size=108, ...}) = 0
lstat64("/usr/home/junjie2", {st_mode=S_IFDIR|0777, st_size=36864, ...}) = 0
lstat64("/usr/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
getcwd("/usr/home/junjie2", 4096) = 18 // get current working directory (这个就是include_path 中的 "." )
lstat64("/usr/home/junjie2/./b.php", {st_mode=S_IFREG|0644, st_size=7, ...}) = 0 // 找见了
lstat64("/usr/home/junjie2/b.php", {st_mode=S_IFREG|0644, st_size=7, ...}) = 0
open("/usr/home/junjie2/b.php", O_RDONLY) = 3
=====
getcwd("/usr/home/junjie2", 4096) = 18 // 这次包含没有真实的文件操作,因为包含过了的文件PHP自己都知道,可以使用
get_included_files()来查看,所以就不需要再lstats和open了=====
getcwd("/usr/home/junjie2", 4096) = 18 // get current working directory (这个就是include_path 中的 "." )
lstat64("/usr/home/junjie2/./c.php", 0xbf904350) = -1 ENOENT (No such file or directory) // 没找见
lstat64("/usr/local/lib/php/c.php", 0xbf904350) = -1 ENOENT (No such file or directory) // 去include_path 中的 "/usr/local/lib/php" 下面找, 还没找见
lstat64("/usr/home/junjie2/c.php", 0xbf904350) = -1 ENOENT (No such file or directory) // 去脚本所在的目录下面找,此次查找和include_path 无关
getcwd("/usr/home/junjie2", 4096) = 18 // 这里又查一次,why?
lstat64("/usr/home/junjie2/./c.php", 0xbf9042a0) = -1 ENOENT (No such file or directory)
lstat64("/usr/local/lib/php/c.php", 0xbf9042a0) = -1 ENOENT (No such file or directory)
lstat64("/usr/home/junjie2/c.php", 0xbf9042a0) = -1 ENOENT (No such file or directory)
getcwd("/usr/home/junjie2", 4096) = 18
lstat64("/usr/home/junjie2/c.php", 0xbf906390) = -1 ENOENT (No such file or directory) // 这次查找可能是报错的时候查的
open("/usr/home/junjie2/c.php", O_RDONLY) = -1 ENOENT (No such file or directory)
结论:
1. 先查找 include_path 设置的目录; 其中的 "." 代表的不是脚本所在目录,而是脚本运行时的cwd(当前工作目录),这个目录是可以通过chdir修改的
2. 如果include_path 中查不到,则再查找脚本所在目录,这个和是否设置了include_path 无关
3. ini_set("include_path", ""); 第二个参数不能为空,为空的话就没有任何作用
关于PHP的一个小题目:
输出结果: 1234 还是 123 ?
答案: 123
如果才能达到 1234 的效果呢?
使用list、each, 代码如下:
- <?php
- $arr = array(1,2,3);
- foreach($arr as $num) {
- if ($num == 2) $arr[] = 4;
- echo $num;
- }
答案: 123
如果才能达到 1234 的效果呢?
使用list、each, 代码如下:
- <?php
- $arr = array(1,0,null,2,3);
- while(list($key, $num) = each($arr)) {
- if ($num == 2) $arr[] = 4;
- echo $num;
- }
bug描述: http://www.laruence.com/2011/12/29/2412.html
相关资料: http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html
就像上面资料中提到的,构造hash冲突是利用了PHP Hashtable对数值key的简单处理实现的。如果用字符串key来构造hash冲突,似乎就比较麻烦了; 严格地将,这个和PHP中的hash算法没有必然联系,对于字符串key是通过hash函数计算出一个无符号整型数然后和Hashtable的size取模的,而对于数字key是直接使用该数字与Hashtable的size取模的。
有些同学通过限制post数据的大小来从一定程度上环节带来的危害,限制post数据大小为不超过100k,下面来分析一下这个数字是否够用:
脚本1:
- <?php
- $size = pow(2, 15); // 可以通过第二个参数来改变提交的数据量的大小
- $data = '';
- for ($key=0, $maxkey=($size-1) * $size; $key<= $maxkey; $key+=$size) {
- $data .= $key.'=&';
- $i++;
- }
- echo "key num: $i \n";
- echo "data length:". strlen($data);
脚本2:
- <?php
- $size = pow(2, 15); // 16 is just an example, could also be 15 or 17
- $startTime = microtime(true);
- $array = array();
- for ($key = 0, $maxKey = ($size - 1) * $size; $key <= $maxKey; $key += $size) {
- $array[$key] = 0;
- }
- $endTime = microtime(true);
- echo 'Inserting ', $size, ' evil elements took ', $endTime - $startTime, ' seconds', "\n";
- $startTime = microtime(true);
- $array = array();
- for ($key = 0, $maxKey = $size - 1; $key <= $maxKey; ++$key) {
- $array[$key] = 0;
- }
- $endTime = microtime(true);
- echo 'Inserting ', $size, ' good elements took ', $endTime - $startTime, ' seconds', "\n";
80k的数据就可以构造8000 个key,耗费cpu时间大约不到2s;
360k的数据可以构造3.2万个key,耗费cpu时间大约30多s;
看来限制 100k 的数据还是非常有效的。
关于PHP Hashtable的更多参考资料:
http://www.qingliangcn.com/2009/07/php%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8Bhashtable/
http://www.phpchina.com/index.php?action-viewthread-tid-88505
---------------------
关于PHP Hashtable的几点提示:
1. Hashtable的最小大小为8 ,如果元素个数为9,则大小重新调整为16,每次调整都要遍历所有元素,重新计算hash值
2. 数值key不做hash,直接和Hashtable 的size取模
3. nNextFreeElement 用于数字索引的计数,其值为当前数字索引值加1,初始值为0
4. pNext, pLast 是hash冲突时的冲突链表的双向指针
5. PListNext, pListLast 是用于遍历数组的双向链表指针
6. pInternalPointer 是用于遍历数组的一个位置指针




2012/05/19 22:43 | 
