之前写过一个算数独的程序,不过有点bug。前些日子有人突然又问起了这个程序于是改了一下,现在应该没啥大问题了,对于任何一个有唯一解的题都能很快给出答案。
-
数独求解程序 php版
2008-06-20 14:49 -
php-fpm文档中文翻译
2008-05-01 16:33原文链接:http://php-fpm.anight.org/
fast-cgi是做啥用的
FastCGI是一个可伸缩的,高速地在web server和脚本语言间交互的接口。关于FastCGI技术的更多信息可以在官方网站和这里看到。
多数流行的web server都支持FastCGI。包括Apache(mod_fastcgi和mod_fcgid),Zeus,nginx和lighttpd。
FastCGI的主要优点是把动态语言和web server分离开来。这种技术允许把web server和动态语言运行在不同的主机上,以大规模扩展和改进安全性而不损失生产效率。
php-fpm可以和任何支持远端FastCGI的web server工作。
php-fpm是做啥用的
很不幸,官方网站php.net上的php在将FastCGI SAPI用于生产环境时有许多已知的问题。
下面的一些证据足以表明为什么离开了php-fpm,就很难使用FastCGI SAPI。
描述 php自带的 spawn-fcgi + spawn-php.sh + daemontools php-fpm php守护程序: pid file, log file, setsid(), setuid(), setgid(), chroot() (-) (+) (+) 进程控制,可以平滑地重启、重新载入配置和二进制模块而不丢失请求 php4 - php5 只能平滑停止 (-) (+) 限制ip地址,可以满足web server的要求 php4 (-) php5 (+) (从 5.2.2 开始) (-) (+) 根据负载动态调整进程数 (-) (-) Todo 使用用不同的uid / gid / chroot / 环境变量,不同的 php.ini 选项,不需要safe mode (-) (-) (+) 记录work process的stdout和stderr (-) (-) (+) 如果使用优化器,在遇到opcode缓存随机损坏的时候紧急重启所有进程 (-) (-) (+) 如果set_time_limit没有起作用,强制结束过期进程 (-) (-) Todo 特色功能 Error header、优化的上传支持、fastcgi_finish_request() 特色功能
所有的这些功能都是用不打断的方式。也就是说,如果你不使用他们,他们的存在不会影响php的功能,也就是透明的。
Error header
类型:方便
默认情况下,如果被访问的php脚本包含语法错误,用户会收到一个空的"200 ok"页。这是不方便的。Error header这个php.ini选项允许在这种情况下产生一个HTTP错误码,比如"HTTP/1.0 550 Server Made Big Boo",从而中断web server请求并显示一个正确的错误页。如果要实现这样的功能,需要在php.ini中添加一条fastcgi.error_header = "HTTP/1.0 550 Server Made Big Boo"
在php-5.2.4中添加了类似的功能,不过是简化的。如果被访问的php脚本包含语法错误,并且display_errors设为false,且没有进一步的设置,会立刻返回"HTTP/1.0 500 Internal Server Error"。如果你需要设定一个不同于500的错误码(已经在许多情况下被使用)。或者想要使这个行为独立于display_errors的设置,那么可以使用fastcgi.error_header。如果你同时使用php-5.2.5或以上版本和php-fpm,那么fastcgi.error_header的优先级更高。
优化的上传支持
类型:优化
这个特性,就如标题那样,可以加速大POST请求的处理速度,包括文件上传。优化是通过确保请求体已写入一个临时文件,然后传递文件名而不是请求体到fastcgi协议来实现的。目前,就我所知,只有nginx0.5.9以上才支持这个功能。显然,这种模式只在php和web server在一台机器上的时候才能用。
nginx样例配置:
location ~ \.php$ {
fastcgi_pass_request_body off;
client_body_in_file_only clean;
fastcgi_param REQUEST_BODY_FILE $request_body_file;
...
fastcgi_pass ...;
}在php中不需要配置任何东西。如果php收到了参数REQUEST_BODY_FILE,就读取其中的请求体,如果没有,就自行从fastcgi协议中读取请求体。
结合这个特性,可以考虑对临时文件使用内存文件系统,例如tmpfs(linux):
client_body_temp_path /dev/shm/client_body_temp;
fastcgi_finish_request()
类型:优化
这个特性可以提高php处理请求的速度。如果有些处理可以在页面生成完后进行,就可以使用这种优化。比如,在memcached中保存session就可以在页面交给web server后进行。fastcgi_finisth_request(),这一特性可以结束响应输出,web server可以立即开始交给等不及的客户端,而此刻,php可以在请求的上下文环境中处理许多事情。比如保存session,转换上传的视频,处理统计等等。
fastcgi_finisth_request()会触发shutdown 函数运行。
FAQ
Q:php-fpm可以和ZendOptimize一起用吗?
A:完全可以。Q:php-fpm可以和ZendPlatform、xcache、eAccelerator、APCden等等的优化器一起用吗?
A:是的。php-frpm的架构和任何一种用于高速opcode缓存的共享内存都适用。唯一的限制是:所有的worker进程只能适用一个缓存,即使它们用不同的uid/gid运行。Q:为什么要用root运行php-fpm呢?这安全吗?
A:用root启动php-fpm只有在你打算用不同uid/gid的php来处理请求时才有意义意。比如,在共享主机上的不同站点。因为只有在master进程用root运行的时候,才可以建立不同uid/gid的子进程。这是相当安全的。master进程自己从来不会去处理请求。
在任何情况下,php-fpm都不会用root身份来处理请求。Q:php-fpm可以加速请求处理速度吗?
A:不,没有影响。不过,如果你使用一些特殊特性,对于一些特定的请求可以有些许性能提升。Q:php-fpm将来会被官方的php包含吗?
A:我很尊重php开发团队和他们的工作。相信他们能做得完美。但不幸的是,就我的经历看来,他们太忙了,似乎讨论php-fpm这个补丁会用相当多的时间,而我则完全没时间。简要说明:
php-fpm是根据最小惊奇原则构建的。这是对于缺乏文档的唯一理由。我保证很快会建立一个wiki。如果你有问题的话,请不要犹豫在上面列出的邮件组里写邮件。
php-fpm已经在linux、macosx、Solaris和freebsd上测试通过。
确信libxml2(在某些系统上叫做libxml2-devel)已经安装。
下载php和php-fpm$ bzip2 -cd php-5.2.5.tar.bz2 | tar xf -
$ gzip -cd php-5.2.5-fpm-0.5.7.diff.gz | patch -d php-5.2.5 -p1
$ cd php-5.2.5 && ./configure --enable-fastcgi --enable-fpm
$ make all install
编辑$prefix/etc/php-fpm.conf
运行$prefix/bin/php-cgi --fpm
检查$prefix/logs/php-fpm.log的细节
运行phpinfo()检查你的网站是否正常
master进程的pid被存放在$prefix/logs/php-fpm.pidmaster进程可以理解以下信号:
SIGINT, SIGTERM 直接终止
SIGQUIT 平滑终止
SIGUSR2 平滑重载所有worker进程并重新载入配置和二进制模块
译注:创建补丁后,会产生一个补丁文件。在./configuire后会提示你打上。configure比较费时,如果直接打上补丁再./configure可以省些时间。
php-fpm还带有一个更方便的脚本,在$prefix/sbin/php-fpm。可以用php-fpm start|graceful|restart|stop来维护。稍编辑一下就可以让它使用配置文件。这个文档原来基本都是俄文的。我是用google翻译先弄成英文,然后再翻成中文。这当中会产生些错误,可能是google的,也可能是我的。如果你发现了,请指出,谢谢。
p.s. 先感谢下digitalsonic帮我挑了几个错。
-
php扩展开发学习笔记 8
2008-03-07 22:36hash
在php中,许多东西都是hash表。除了显而易见的数组以外,其实对象的属性,函数入口表、变量的符号表等在php内部也都是使用hash表来保存的。事实上,hash表里可以放任何一种数据类型的指针,并不限于zval*。
hash表的C结构叫HashTable。如果要创建一个HashTable,一般的做法如下:
HashTable *ht;
ALLOC_HASHTABLE(ht);
这样就会创建一个HashTable,并把地址放在ht里。光有一个HashTable结构还是不行的,还需要初始化。这就需要用到zend_hash_init函数。声明如下:
int zend_hash_init(
HashTable *ht,
uint nSize,
hash_func_t pHashFunction,
dtor_func_t pDestructor,
zend_bool persistent
)
第一个参数就是一个HashTable指针,必须是已经分配好内存的。
nSize是初始大小,就是存放东西的个数。超过这个值时会自动扩展。如果这个值不是2的n次方,则会自动变成大于它的最小的2的n次方数。
pHashFunction是没用的,但为了向下兼容,这个参数还在那里。必须为NULL。
pDestructor是析构函数。一般使用ZVAL_PTR_DTOR。
最后那个参数persistent是表示是用emalloc还是pemalloc来分配内存。php的hash表可以有整数和字符串两种键,然后就有了两套访问hash表的函数。hash相关函数都以zend_hash_开头。例如:
int zend_hash_update(HashTable *ht, char *arKey, uint nKeyLen, void *pData, uint nDataSize, void **pDest);
用来更新字符串键的值。arKey是键;nKenLen是键的长度,包括最后的'\0';pData是值数据的指针,nDataSize是数据指针的大小。最后一个参数是用来指定值数据指针的保存位置的,对于更新操作一般用NULL即可而对于读取操作,则用来放取出的值的指针。一个调用的例子:
zend_hash_update(pHash, "hello", sizeof("hello"), &pZval, sizeof(zval*), NULL);
其他几个hash操作函数也大同小异。
zend_hash_index_update用来更新整数键的值,zend_hash_find和zend_hash_index_find分别用来读取字符串键和整数键所对应的值;zend_hash_exists和zend_hash_index_exists用来判断指定的键是否存在;zend_hash_del和zend_hash_index_del用于删除。还有许多其他的hash操作函数。zend_hash_num_elements用于得到元素的个数;zend_hash_clean用于清除全部元素;zend_hash_destroy除了清除全部元素外,还会释放由zend_hash_init所分配的内存,彻底销毁hash表。 -
php扩展开发学习笔记 7
2008-02-21 21:30引用计数
php采用的内存管理和垃圾回收方法是基于引用计数的。之前说过,在zval结构里有一个refcount是表示引用计数,还有一个is_ref表示是否是个引用变量。那么php代码的实际运行中,又是如何处理的呢?
比如这样的php代码:
$a = "hello";
$b = $a;
这时候并不像很多人认为的那样,在内存里把"hello"这个字符串复制了一份,而只是把$b指向了和$a对应的同一个zval,然后把那个zval的refcount + 1。这样避免了一次内存拷贝。但如果在这之后改变了其中一个变量的值,比如$b.= " world";又会如何呢?这时候才会分配一个新的zval给$b,然后把原先那个zval的refcount - 1。这就是传说中的copy on write。就是说,在改变值得时候才会有内存拷贝。那么引用变量又会如何呢? 比如
$a = "hello";
$b = &$a;
和前面一样,$a, $b还是指向同一个zval。只是还要把这个zval的is_ref置为1。之后再改变$a或者$b的时候就不会再发生拷贝。那么
$a = "hello";
$b = &$a;
$c = $a;
这时又会如何呢?因为$c并不是一个引用变量,因此不能和$a, $b共用一个zval。因此在$c = $a的时候会直接产生一个新的zval。因此,在php中,使用引用对改善性能并不会有多少作用,通常情况下还会使情况更糟。所以,引用还是只在真正需要的时候才用为好。
再说说垃圾回收。每个zval都有一个refcount表示它的变量的引用数。不管对于普通变量还是引用变量都是如此。refcount的初始值一般为1。每当增加一个引用时就+1,减少一个引用,比如unset时就会-1。当refcount为0的时候,php就会把它释放掉。这就是基于引用计数的垃圾回收方法。
使用zval
初始化zval
MAKE_STD_ZVAL(zval*);
这个宏的左右是创建一个zval,完成初始化(如将ref_count置为1,isref置为false)并把指针赋给参数。赋值
写扩展的时候不可避免的要用到把一个zval复制到另一个zval,就是类似$a = $b;的操作。对于简单的值或许手动维护引用计数之类的还不算很麻烦但对于数组,对象之类的就需要一层层递归进去,因此就有了一个zval_copy_ctor来做着件事情。
原有一个zval* p_zval_b,
zval* p_zval_a;
MAKE_STD_ZVAL(p_zval_a); //初始化p_zval_a
*p_zval_a = *p_zval_b;
zval_copy_ctor(p_zval_a);
这里,zval_copy_ctor完成了类似赋值的操作,包括引用计数处理,对于hash值的成员处理等。释放一个zval则是使用
zval_ptr_dtor(**zval)。注意它的参数。它会释放掉为这个zval所分配的内存。 -
php扩展开发学习笔记 6
2008-02-14 21:40内存管理
在C语言里,分配内存有一组alloc函数,比如malloc、calloc、realloc。php为了更方便,更安全地管理内存,自己提供了一组内存分配的函数:emalloc、ecalloc、erealloc,此外还有estrdup用于拷贝内存,efree用于释放由前面那些函数分配的内存。他们的用法和标准C函数的一样。这组e开头的函数分配的内存空间只存在于一次请求。在请求结束后,php会自动释放掉。这就减少了由于没有释放掉内存而造成的内存泄露的可能。
如果需要分配的内存在整个进程运行时都保持,而不是在请求处理结束后自动释放,则还有一组pe开头的函数,如pemalloc、pecalloc等。这组函数比标准C函数的多一个参数,即分配的内存是否持久,当这个参数为1时,即表示分配的是持久的内存,不会随着请求结束而自动释放。如:pemalloc(sizeof(long) * 100, 1);。
此外,为了方便使用,还提供了safe_emalloc和safe_pemalloc这两个函数。 safe_emalloc的原型如下:void *safe_emalloc(size_t size, size_t count, size_t addtl);。参数分别表示单元的大小,单元的个数,以及偏移。实际分配的空间大小就是size * count + addtl。safe_pemalloc与之类似。这两个函数还有一个作用就是避免手工做上面的计算时,可能造成整数溢出,而导致分配的内存大小小于预期或者是个负数。
-
php扩展开发学习笔记 5
2008-01-28 21:02有许多函数在php手册里把参数或返回值类型写为mixed。那么在扩展中是如何处理的呢?所谓mixed往往就是直接解析会返回zval。
先看参数处理,还是前面用过的zend_parse_parameters:
zval* item;
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &item);
这很简单。然后看返回值,返回zval可以用RETURN_ZVAL宏。和前面的RETURN_LONG之类不同的是,这个宏有3个参数。第一个参数就是一个zval*。第二个和第三个参数都是0、1值。第二个参数是表示是否在返回前复制该值;第三个参数是表示在返回前是否调用析构函数。通常的用法是RETURN_ZVAL(p_zval, 1, 0);。为什么一般要把复制zval设为1呢?这和php的内存管理有关,关于内存管理后面会说。这里大概说下,因为php的垃圾回收是基于引用计数的。如果不复制一份,在函数结束后,因为返回值也是一个zval的指针,结束后php会把这个zval的引用计数-1,在一般情况下,这就会导致释放掉不该释放掉的内存,造成错误。
-
php扩展开发学习笔记 4
2008-01-13 10:33zval和zvalue_value
之前一直跳过的数组和对象,以及需要返回或者处理多种类型时,都需要了解php内部是如何保存值的。
php内部保存值的基本单位是zval。它的C结构定义如下:
typedef struct _zval_struct {
zvalue_value value;
zend_uint refcount;
zend_uchar type;
zend_uchar is_ref;
} zval;其中is_ref表示这个值是否是一个引用。
type表示值类型:有IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT、IS_RESOURCE这8种。
refcount表示引用计数。
value存放这个zval的值。在判断一个zval的值时,可以用如下的代码:
zval *a;
...
if (Z_TYPE_P(a) == IS_LONG){
...
}zvalue_value中保存了zval的值,它的C结构定义如下:
typedef union _zvalue_value {
long lval;
double dval;
struct {
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
} zvalue_value;这是一个union型,其中的lval、dval、str、ht、obj分别表示整数、浮点、字符串、数组、对象。
取 得一个zval的值当然也可以直接从它的value里直接取,不过更好的办法是通过一套宏来获取。Z_BVAL、Z_LVAL_P、Z_DVAL_PP这 样的形式。都以Z_开头,后面是类型,B就是boolean,L就是long,D就是double。后面可选的_P,_PP表示指针的层次。带_P是用来 取zval*的,带_PP是用来去zval**的。这些宏返回的就是这个zval的值所对应的C类型。对于字符串,则有两组宏Z_STRVAL、 Z_STRLEN。后面当然也可以加_P和_PP。分别返回char*和字符串的长度。这些宏使用很简单,例如:
zval* a;
...
long n;
n = Z_LVAL_P(a);前面用到的Z_TYPE_P其实也是一个类似的宏,用于取得zval*的类型
-
php扩展开发学习笔记 3
2008-01-04 22:42取得参数
既然是函数总要能获取参数。在php扩展里能通过zend_parse_parameters()将参数解析为一些C的对应类型。
如
long foo;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &foo) == FAILURE) {
RETURN_NULL();
}
会试图解析参数为整数。将值放入foo。失败的时候,比如没有参数,或参数不能解析为整数时返回FAILURE。这里的"l"表示整数。其他如"b","d","s"分别表示布尔,浮点和字符串。资源数组对象等以后再说。
其中,布尔对应的C类型是zend_bool。其实就是0,1。浮点对应的是double。
对于字符串,则需要传入两个参数,分别用于存放字符串的值和长度。如
char *name;
int name_len;
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
&name, &name_len);
此处由zend_parse_parameters分配的内存不需要手动释放。如果需要解析多个参数,可以如下面的例子:
long foo;
char *name;
int name_len;
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl",
&name, &name_len, &foo); -
php扩展开发学习笔记 2
2007-12-29 21:30函数返回值
php的api里定义了很多宏来实现扩展里函数的返回值。RETURN_BOOL、RETURN_LONG、RETURN_DOUBLE这3个宏分别用于返回对应的值。只在参数里填入需要返回的值即可。如RETURN_LONG(1024);。对于string,则有RETURN_STRING和RETURN_STRINGL。其中后者可以指定字符串的长度,前者只是以\0作为结束标志。第二个参数表示是否复制字符串的值。如果char数组是在栈空间内分配的(如作为局部变量的字符串常量)就需要指定为1。
实际上,php的扩展中,函数的返回值是通过一个return_value的变量来传递的。如果煮一下PHP_FUNCTION所展开的内容的话,会发现return_value实际上是这个函数的一个参数。前面那几个宏所作的事情就是,对return_value赋值然后return。对return_value赋值则还有一组RETVAL_开头的宏。如RETVAL_BOOL等。
-
php扩展开发学习笔记 1
2007-12-25 21:27开发环境设置见《用vc 2008编译php扩展》。
写一个扩展最基本的就是编写函数。我这里是用skel生成了一个algorithm的扩展的骨架。
php扩展中的函数用PHP_FUNCTION宏定义。首先在.h文件中写一个定义,如skel生成的代码为例:PHP_FUNCTION(confirm_algorithm_compiled);
然后在.c文件中写函数的实现。
PHP_FUNCTION(confirm_algorithm_compiled)
{
//...
}
这和传统的C编程很像。括号内就是函数的名字。这里没有参数列表,函数的参数是通过其他途径获取的。然后,还需要在扩展的函数入口表里添加一条:PHP_FE(confirm_algorithm_compiled, NULL)。这样在php里才能找到这个函数。这里FE应该就是function entry的缩写。zend_function_entry algorithm_functions[] = {
PHP_FE(confirm_algorithm_compiled, NULL)
{NULL, NULL, NULL} /* Must be the last line in algorithm_functions[] */
};这里的{NULL, NULL, NULL}的作用如skel生成的代码中的注释所说,是函数入口表的结束标志。




