Archive for 三月, 2012

适当的时候,说No

修行佛法,要行六度,布施、持戒、忍辱、精进、禅定、般若。过去的一年,随便捡一件与同事之间的冲突,都可以成为六度的反面。

以与同事就救助流浪小动物的分歧为例。导火索仅仅是,我邀请她同我一起去看望一个救助者,她拒绝了,并要求我提供最近一段时间买猫粮、狗粮的发票。事情的表面很简单,暗流很多,而从我的角度考虑,我无法忍受并最终离职的原因,是因为我不喜欢这个同事。为什么不喜欢她呢?因为之前,强迫我做了很多我不想做的事情。那为什么,我明明不想做,却还做了呢?如果在一件件事情的开始,就与她商量,哪些可以不做,或者换种方式做,或者接受其意图、从而心甘情愿地做,或者做了、但使她知道我的难处、使其下次可以体谅,那也许不会有最终的这些矛盾。

事情都是双面的,她有不对,但即使这样,我也不应该揪住不放;我也有很多不对,自当忏悔,悔之前种种不如法的行为,更要找出根源,以后杜绝、不再犯!

但学佛,不是一蹴而就的事情,得一步一步走。以我的根性而言,如果当下就以佛菩萨的标准来要求自己,必然会起退转心、生烦恼。所以,我得学会善呵护自己的心,犹如调琴师一样,不紧不松。

忍辱,从金刚经而言,最高境界应该是,无忍辱。即,不觉得忍辱这种身口意的行为,会被称之为“忍辱”,只是自自然地做去。我万万达不到忍辱,我对人的一开始态度是忍让,你要求我做什么都可以。但是,这种忍让,更多是行为上的,而内心是忍耐。这样的状态,一时可以,如果对方也是随和的人,有来有往,我们就会相处融洽。反之,则次数和时间积累之后,就会超出我能承受的底线,于是生嗔心,进而反映到行为和言语上。这样看来,哪里有“我”?完全是随着外境、遇到什么样的人,来决定了我与他的关系!

与其这样压抑而后在某一刻爆发,不如在开始,我尚能控制情绪的时候,心平气和的拒绝一些我无法接受的要求。只要是在一种平稳的心理状态下,这就是如法的!多一些这样的锻炼,势必能更好的主宰我的心,会使拒绝来得更少、更晚,与人相处,也会更平和。

那怎样判断这个拒绝的时机呢?一个是,敏锐地察觉自己是否有嗔心起,如果是的话,并且无法平息嗔心,可能需要。另一个是,这个要求,是否是一个如法的要求,还是可有可无、仅仅增长世俗关系的要求?如果我一时的忍耐,能够对佛法僧、多人、多事有利益的话,那忍耐是可以接受的。否则,如果这个要求让我很不舒服,那也可能需要拒绝。敏锐的觉知,得从禅定中获取。是否如法的判断,依赖于般若智慧和善知识的指点。

忍辱,同时也依赖于持戒。如果没有戒律,嗔心一起,杀盗淫妄酒都可能起了,而持戒,至少可以使行为和言语上有所顾忌。另一方面,多布施,与众生结善缘,也可以使他们对我减少敌意,有一个平和的环境,当然更有利于修行。

以上种种,如果只是当作借口,那就是放逸的!但,如果出发点,是为了更好的修行,那就是精进。做事的原则,无绝对。适当的时候说No,得在佛法的指导下进行!

再美好的,也让它去吧

其实,我那个同事,真的是我的善知识。之前种种的不如意,现在看来,都可以对境修行。

依然以救助流浪动物为例,她有给我发过一封信,提出了一些她认为很好的经验,希望能够应用到我们的救助行为中,并影响到被救助者。我没有回这封信,因为不知道该怎么回。

昨天,夕阳下,经过一片广袤的田野,看到空中掠过飞鸟的剪影。很美好!那瞬间,就觉得,当下的美好赞叹完了,下一刻如果还执着于这种美好,或描述给别人并希求得到共鸣,或与人争论哪种景色更为美好,或满世界找寻希望重现,如此种种,就不如法了。

像我现在,其实也是在着相于“再美好的,也让它去吧”这句话,尽力的描述我的感悟,其实也不如法。但我乃凡夫,怕这一刻的小小体悟,下一刻再犯,所以强行记下来罢了。

再赘述一二。同事的那封信,实际有让我起小小的烦恼,或许是我妄加的揣度,觉得她要把某种标准强加给别人。其实标准本身好的,只是说的方式、时间、对象,有所偏差,就会造成相反的效果。我为人处世,之前也有很多自认为道德标准的东西,也常常强迫别人接受我的标准,其实已造了不知多少业,使别人生了多少烦恼呢!以他人为镜,今后我也要多加小心!

最近脑袋发木,找一些脑筋急转弯的题目来写写。

题目:有一个整数数组,求其连续子数组的最大和。

#include <stdio.h>
#include <strings.h>
#include <math.h>

#define MAXLEN 1024

int arr[MAXLEN], num=0;

int read2arr(){
        bzero(arr, MAXLEN * sizeof(int));
        int *p=arr;

        while(!feof(stdin)){
                if(++num > MAXLEN){
                        fprintf(stderr, "Too many numbers!");
                        return -1;
                }
                scanf("%d", p++);
        }
        num--;

        /*
        for(p=arr; p<arr+num; ++p){
                printf("%d\n", *p);
        }
        */
        return 0;
}

long maxsum(){
        long max = (long)-pow(2,31), sum=0;
        int i = 0;

        for(;i<num;++i){
                if(sum + arr[i] > 0){
                        sum += arr[i];

                        if (sum > max){
                                max = sum;
                        }
                } else{
                        sum = 0;
                }
        }

        return max;
}

int main(int argc, char** argv){
        if(read2arr() < 0){
                return -1;
        }

        printf("max child sum: %ld\n", maxsum());

        return 0;
}


思路是,依次遍历数组时,如果前面累积的和为负数或0,则只会使后面的数之和变小,所以可以抛弃。

nginx能够通过upstream的方式,把请求分配到不同的phpcgi服务上。

#phpcgi/etc/php-fpm.conf
listen = 10.241.133.144:9000   # 本地ip,非127.0.0.1
listen.allowed_clients = 10.241.133.137 # nginx server ip,非127.0.0.1

#nginx/conf/nginx.conf
upstream multiphp{
  server 10.241.133.144:9000 weight=1;
  server 127.0.0.1:9000 weight=1;
}
server{
  ……
  location ~ .*\.php{
    fastcgi_pass  multiphp;
    #fastcgi_pass  127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include fastcgi.conf;
  }
}

我们的服务器迁移到了万网,使用其负载均衡,发现其remote_addr传递的是内网ip,应该是负载均衡的内网出口(但是有6个,之后可以学习下是为什么)。而真实的用户ip是存储在php的$_SERVER[‘HTTP_REMOTEIP’]里。
查看nginx内置变量,与proxy相关的是http_x_forwarded_for,对应该HTTP_REMOTEIP。故修改access_log format如下:

    log_format access '$remote_addr $request_time [$time_local] "http://$host$request_uri" $status $body_bytes_sent "$http_referer  " "$http_user_agent" $http_x_forwarded_for';
    access_log  logs/access.log access;

nginx常用变量在源码的src/http/ngx_http_variables.c 文件的ngx_http_core_variables变量里。
static ngx_http_variable_t  ngx_http_core_variables[] = {

    { ngx_string("http_host"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.host), 0, 0 },

    { ngx_string("http_user_agent"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 },

    { ngx_string("http_referer"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.referer), 0, 0 },

#if (NGX_HTTP_GZIP)
    { ngx_string("http_via"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.via), 0, 0 },
#endif

#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP)
    { ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },
#endif

    { ngx_string("http_cookie"), NULL, ngx_http_variable_headers,
      offsetof(ngx_http_request_t, headers_in.cookies), 0, 0 },

    { ngx_string("content_length"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.content_length), 0, 0 },

    { ngx_string("content_type"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },

    { ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 },

    { ngx_string("binary_remote_addr"), NULL,
      ngx_http_variable_binary_remote_addr, 0, 0, 0 },

    { ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 },

    { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },

    { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },

    { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },

    { ngx_string("server_protocol"), NULL, ngx_http_variable_request,
      offsetof(ngx_http_request_t, http_protocol), 0, 0 },

    { ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 },

    { ngx_string("request_uri"), NULL, ngx_http_variable_request,
      offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },

    { ngx_string("uri"), NULL, ngx_http_variable_request,
      offsetof(ngx_http_request_t, uri),
      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("document_uri"), NULL, ngx_http_variable_request,
      offsetof(ngx_http_request_t, uri),
      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("request"), NULL, ngx_http_variable_request_line, 0, 0, 0 },

    { ngx_string("document_root"), NULL,
      ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("realpath_root"), NULL,
      ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("query_string"), NULL, ngx_http_variable_request,
      offsetof(ngx_http_request_t, args),
      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("args"),
      ngx_http_variable_request_set,
      ngx_http_variable_request,
      offsetof(ngx_http_request_t, args),
      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("is_args"), NULL, ngx_http_variable_is_args,
      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("request_filename"), NULL,
      ngx_http_variable_request_filename, 0,
      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("server_name"), NULL, ngx_http_variable_server_name, 0, 0, 0 },

    { ngx_string("request_method"), NULL,
      ngx_http_variable_request_method, 0,
      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("remote_user"), NULL, ngx_http_variable_remote_user, 0, 0, 0 },

    { ngx_string("body_bytes_sent"), NULL, ngx_http_variable_body_bytes_sent,
      0, 0, 0 },

    { ngx_string("request_completion"), NULL,
      ngx_http_variable_request_completion,
      0, 0, 0 },

    { ngx_string("request_body"), NULL,
      ngx_http_variable_request_body,
      0, 0, 0 },

    { ngx_string("request_body_file"), NULL,
      ngx_http_variable_request_body_file,
      0, 0, 0 },

    { ngx_string("sent_http_content_type"), NULL,
      ngx_http_variable_sent_content_type, 0, 0, 0 },

    { ngx_string("sent_http_content_length"), NULL,
      ngx_http_variable_sent_content_length, 0, 0, 0 },

    { ngx_string("sent_http_location"), NULL,
      ngx_http_variable_sent_location, 0, 0, 0 },

    { ngx_string("sent_http_last_modified"), NULL,
      ngx_http_variable_sent_last_modified, 0, 0, 0 },

    { ngx_string("sent_http_connection"), NULL,
      ngx_http_variable_sent_connection, 0, 0, 0 },

    { ngx_string("sent_http_keep_alive"), NULL,
      ngx_http_variable_sent_keep_alive, 0, 0, 0 },

    { ngx_string("sent_http_transfer_encoding"), NULL,
      ngx_http_variable_sent_transfer_encoding, 0, 0, 0 },

    { ngx_string("sent_http_cache_control"), NULL, ngx_http_variable_headers,
      offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },

    { ngx_string("limit_rate"), ngx_http_variable_request_set_size,
      ngx_http_variable_request_get_size,
      offsetof(ngx_http_request_t, limit_rate),
      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version,
      0, 0, 0 },

    { ngx_string("hostname"), NULL, ngx_http_variable_hostname,
      0, 0, 0 },

    { ngx_string("pid"), NULL, ngx_http_variable_pid,
      0, 0, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }
};

我们有4台机器,通过NFS共享文件。

今天在系统迁移过程中,误把NFS server的目录删除了,从而导致在client机上ls /path/to/nfs/时显示:Stale NFS file handle。umount -l (lazy umount)或者umount -f (Force umount)之后,仍然无法解决该问题。

搜索的结果是,由于有进程使用着这个目录导致的,所以重启nfs相关命令即可:

/etc/init.d/portmap restart

/etc/init.d/nfs restart

/etc/init.d/nfslock restart

我记得linux中有防止删除的粘滞位,回头看看是否合用。

上周我们的数据库瞬间出现了很多locked进程,很快,所有服务器页面都无法打开。我们有两个大的项目部署同时在这几台webserver上,分别访问两台mysql server。

通过查看慢查询log发现,有一条管理后台sql语句执行时间为近800秒,其后有多条locked time占总时间90%以上的慢查询。但是,这条管理后台sql只是select语句,怎么会使其他select语句locked呢?一般思考读锁是相容的啊。经实验发现,该sql句子涉及到多张表,且逻辑比较复杂,需要对一张MyISAM类型的product表加读锁,而该表经常会有写操作,这些写操作尝试加写锁时blocked,但是会wait写锁(之前书上看到过,没有查看mysql资料,大概原理应该相同),这就同时会使后面的读操作加读锁失败。

除了需要对这些sql句子进行优化之外,因为之前进行过一次长时间的mysqldump操作,所以show status like ‘%qcache%’发现该数据的query_cache_type为DEMAND,即只有在显示写成select SQL_CACHE时才会使用到查询缓存。在这种配置下,不存在缓存竞争的问题。

为了解决这种读写锁竞争的问题,做了读写分离。之前两台数据库已经是master-slave备份形式了,但是slave并没有承担线上压力,现在从代码层面上处理。

之前的代码描述如下:

class Core_Mysql{
   function querys{...}
   function query{...}
   function update{...}
}
class Core_Model{
   protected $db;
   function __construct{
       $db = new Core_Mysql();
   }  

   function func1_read{ $sql = '...'; $this->db->querys($sql);}
   function func2_write{ $sql = '...'; $this->db->update($sql);}
}

Core_Model还有多个子类继承并override了__construct方法,但是都没有改变db变量的初始化方式。为了最小限度的重构,代码如下:
class Core_Mysql{
   function querys{...}
   function query{...}
   function update{...}
}
class Core_ModelParent{
   private $db_r;
   private $db_rw;

   function __get($name){ return ('db' == $name)? $this->db_rw: (('db_r' == $name)? $this->db_r: null); }
   function lazy_get_r(){...}
   function lazy_get_rw(){...}
   function set_db_info(){...}
}
class Core_Model{
   protected $db;
   function __construct{
       $db = new Core_Mysql();
   }  

   function func1_read{ $sql = '...'; $this->db_r->querys($sql);}
   function func2_write{ $sql = '...'; $this->db->update($sql);}
}

这样,只需要修改model层里希望访问只读库的方法了。当然,以上只是一个大概的代码,在实际中,还考虑了如果主库挂了,则所有访问转移到从库,但只读(在Core_mysql的update方法里控制即可);或者如果从库挂了,则所有访问指向主库。在故障前后,不需要修改代码。

目前仅将前几天导致故障的那个管理后台sql句子指向了从库,待运行几天稳定后,再将更多的流量导向从库。

也需要考虑到,主从复制是有时延的,所以时间敏感的读操作,还需要保留在主库上。

———————————

这里还有一个问题,由于lazy连接mysql数据库,而在构造sql句子时,我会调用mysql_real_escape_string来封装字符串,在手册里写道:

Note:

A MySQL connection is required before using mysql_real_escape_string() otherwise an error of level E_WARNING is generated, and FALSE is returned. If link_identifier isn’t defined, the last MySQL connection is used.

这就导致会引发如下错误:

PHP Warning:  mysql_real_escape_string() [<a href=’function.mysql-real-escape-string’>function.mysql-real-escape-string</a>]: A link to the server could not be established
PHP Warning:  mysql_real_escape_string() [<a href=’function.mysql-real-escape-string’>function.mysql-real-escape-string</a>]: Access denied for user ‘www-user-name’@’localhost’ (using password: NO)

即,在没有mysql连接时,调用mysql_real_escape_string会触发mysql_connect使用当前用户(webserver运行者)连接本地的数据库。为了解决该问题,可以在Core_model的构造器中显示连接读写库。或者压根不使用该方法,自己写一个替代的方法。

taint是大牛风雪之隅写的一个php扩展,用来检测GET、POST、COOKIE等中可能有的漏洞。其中用了很多php扩展开发必须的知识,简单记录代码逻辑如下。

首先,看到代码最末端的PHP_MINIT_FUNCTION,该方法在每个php进程初始化时执行,如果需要的话,则加载taint扩展模块,包括注册新函数,以及override旧函数,这里主要是一些与字符串有关的函数。前者使用zend_set_user_opcode_handler方法,设置echo、include等的钩子函数。

同样需要注意的是PHP_RINIT_FUNCTION,该方法在每个web请求开始时执行。所以,如果没有sapi支持的话,就不必要检查了。对于POST、GET、COOKIE数组中的每个值,标记它为possible tainted。这样,如果后续有使用到这些值,即op的两端有这些值的话,result也就是possible tainted了。

RINIT里使用php_taint_mark_strings方法标识tainted字符串,在其末尾追加一个unsigned字符0x6A8FCE84作为标识符。在该函数开始,会先检查是否有嵌套:

|     if (++ht->nApplyCount > 1) {
|         ht->nApplyCount–;
|         return;
|     }

不过我不是很明白GET、POST、COOKIE在什么情况下会有嵌套存在。

打标记,由于这里仅改变了Z_STRVAL_PP(ppzval)的内存大小和内容,但是并没有改变Z_STRLEN_PP(ppzval)。所以之后常规方法是用ppzval的时候,不会读取到打上的标记值:

| Z_STRVAL_PP(ppzval) = erealloc(Z_STRVAL_PP(ppzval), Z_STRLEN_PP(ppzval) + 1 + PHP_TAINT_MAGIC_LENGTH);
| PHP_TAINT_MARK(*ppzval, PHP_TAINT_MAGIC_POSSIBLE);

以echo的钩子函数为例,说明单操作符。
static int php_taint_echo_handler(ZEND_OPCODE_HANDLER_ARGS) /* {{{ */ {
    zend_op *opline = execute_data->opline;
        zval *op1 = NULL;
        zend_free_op free_op1;

        switch(TAINT_OP1_TYPE(opline)) {
                case IS_TMP_VAR:
                        op1 = php_taint_get_zval_ptr_tmp(TAINT_OP1_NODE_PTR(opline), execute_data->Ts, &free_op1 TSRMLS_CC);
                        break;
                case IS_VAR:
                        op1 = TAINT_T(TAINT_OP1_VAR(opline)).var.ptr;
                        break;
                case IS_CV: {
                                zval **t = TAINT_CV_OF(TAINT_OP1_VAR(opline));
                                if (t && *t) {
                                        op1 = *t;
                                } else if (EG(active_symbol_table)) {
                                        zend_compiled_variable *cv = &TAINT_CV_DEF_OF(TAINT_OP1_VAR(opline));
                                        if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len + 1, cv->hash_value, (void **)&t) == SUCCESS) {
                                                op1 = *t;
                                        }
                                }
                    }
                        break;
        }

        if (op1 && IS_STRING == Z_TYPE_P(op1) && PHP_TAINT_POSSIBLE(op1)) {
                if (ZEND_ECHO == opline->opcode) {
                        php_taint_error("function.echo" TSRMLS_CC, "Attempt to echo a string that might be tainted");
                } else {
                        php_taint_error("function.echo" TSRMLS_CC, "Attempt to print a string that might be tainted");
                }
        }

        return ZEND_USER_OPCODE_DISPATCH;
} /* }}} */

这里就是检查echo的内容,是否在之前被打伤tainted标记。这里区分不同类型的变量,获取其值。摘录网上的说明如下:

IS_CV:这种类型的操作数比较重要,此类型是在PHP后来的版本中(大概5.1)中才出现,CV的意思是compiled variable,即编译后的变量,变量都是保存在一个符号表中,这个符号表是一个哈希表,试想如果每次读写变量的时候都需要到哈希表中去检索,势必会对效率有一定的影响,因此在执行上下文环境中,会将一些编译期间生成的变量缓存起来,此过程以后再详细介绍。此类型操作数一般以!开头表示,比如变量$a=123;$b=”hello”这段代码,$a和$b对应的操作数可能就是!0和!1, 0和1相当于一个索引号,通过索引号从缓存中取得相应的值。

IS_TMP_VAR:表示临时变量,临时变量一般在前面加~来表示,这是一些OP执行过程中需要用到的中间变量,例如初始化一个数组的时候,就需要一个临时变量来暂时存储数组zval,然后将数组赋值给变量。

IS_VAR: 一般意义上的变量,以$开发表示。

IS_TMP_VAR, 顾名思义,这个是一个临时变量,保存一些op_array的结果,以便接下来的op_array使用,这种的操作数的u保存着一个指向变量表的一个句柄(整数),这种操作数一般用~开头,比如~0,表示变量表的0号未知的临时变量

IS_VAR 这种就是我们一般意义上的变量了,他们以$开头表示

IS_CV 表示ZE2.1/PHP5.1以后的编译器使用的一种cache机制,这种变量保存着被它引用的变量的地址,当一个变量第一次被引用的时候,就会被CV起来,以后对这个变量的引用就不需要再次去查找active符号表了,CV变量以!开头表示。

这样,逻辑就很清晰了!最后,还有一个小疑问,mark taint时分配的那些unsigned字节,会在什么时候释放呢?由于这些空间是用erealloc分配的,初步猜测是在这个php进程结束时被释放。

  1. 启动(重启)mysql:sudo /etc/init.d/mysqld restart –skip-grant-tables,使其不进行权限验证
  2. 修改root密码:
  • mysql> use mysql;
  • update user set password=PASSWORD(‘newpwd’) where user = ‘root';
  • FLUSH PRIVILEGES;
  1. 正常重启mysql:sudo /etc/init.d/mysqld restart

今天在安装php-5.3.10时,make报错:

ext/date/php_date.lo is not a valid libtool object。

查看libtool(由ltmain.sh生成)该错误是由于:

if (${SED} -e ‘2q’ $arg | grep “^# Generated by .*$PACKAGE”) >/dev/null 2>&1; then 语句不成立而后续引起的。

正确的lo文件应该如下:

# ext/date/php_date.lo – a libtool object file
# Generated by ltmain.sh – GNU libtool 1.5.26 (1.1220.2.492 2008/01/30 06:40:56)
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# Name of the PIC object.
pic_object=none
# Name of the non-PIC object.
non_pic_object=’php_date.o’

初始的5行,是有libtool文件中compile时添加的。在用安装脚本运行configure和make时,经常发现未添加成功。这就导致了后面的错误。

而手工make clean后再 make,就没有问题了。

原因未知。会是因为多核、多进程吗?对Makefile和libtool的执行顺序不是很了解,无法确切定位。

当脚本执行时,查看进程如下:

admin    20630  0.0  0.0  65068  2420 pts/2    R+   15:17   0:00 /bin/sh /data/home/admin/src/php-5.3.10/libtool –silent –preserve-dup-deps –mode=compile gcc -IZend/ -I/data/home/admin/src/php-5.3.10/Zend/ -DPHP_ATOM_INC -I/data/home/admin/src/php-5.3.10/include -I/data/home/admin/src/php-5.3.10/main -I/data/home/admin/src/php-5.3.10 -I/data/home/admin/src/php-5.3.10/ext/date/lib -I/data/home/admin/src/php-5.3.10/ext/ereg/regex -I/usr/include/libxml2 -I/data/home/admin/src/php-5.3.10/ext/mbstring/oniguruma -I/data/home/admin/src/php-5.3.10/ext/mbstring/libmbfl -I/data/home/admin/src/php-5.3.10/ext/mbstring/libmbfl/mbfl -I/usr/local/libmcrypt/include -I/usr/local/mysql//include/mysql -I/usr/local/mysql/include/mysql -I/data/home/admin/src/php-5.3.10/ext/sqlite3/libsqlite -I/data/home/admin/src/php-5.3.10/TSRM -I/data/home/admin/src/php-5.3.10/Zend -I/usr/include -g -O2 -fvisibility=hidden -c /data/home/admin/src/php-5.3.10/Zend/zend_hash.c -o Zend/zend_hash.lo

admin    20636  0.0  0.0  65068  2420 pts/2    R+   15:17   0:00 /bin/sh /data/home/admin/src/php-5.3.10/libtool –silent –preserve-dup-deps –mode=compile gcc -IZend/ -I/data/home/admin/src/php-5.3.10/Zend/ -DPHP_ATOM_INC -I/data/home/admin/src/php-5.3.10/include -I/data/home/admin/src/php-5.3.10/main -I/data/home/admin/src/php-5.3.10 -I/data/home/admin/src/php-5.3.10/ext/date/lib -I/data/home/admin/src/php-5.3.10/ext/ereg/regex -I/usr/include/libxml2 -I/data/home/admin/src/php-5.3.10/ext/mbstring/oniguruma -I/data/home/admin/src/php-5.3.10/ext/mbstring/libmbfl -I/data/home/admin/src/php-5.3.10/ext/mbstring/libmbfl/mbfl -I/usr/local/libmcrypt/include -I/usr/local/mysql//include/mysql -I/usr/local/mysql/include/mysql -I/data/home/admin/src/php-5.3.10/ext/sqlite3/libsqlite -I/data/home/admin/src/php-5.3.10/TSRM -I/data/home/admin/src/php-5.3.10/Zend -I/usr/include -g -O2 -fvisibility=hidden -c /data/home/admin/src/php-5.3.10/Zend/zend_hash.c -o Zend/zend_hash.lo

有时,两个libtool同时运行,且命令完全一致!!查看pstree:

有时:

|-sshd-+-sshd—sshd—bash—install.sh—2*[make—sh—gcc—cc1]

有时:

|-sshd-+-sshd—sshd—bash—install.sh-+-make—sh

|      |                                 `-make—sh—gcc—cc1

这时,再ps axu|grep make,发现居然同时有make和 make install两个命令!!

查看脚本,果然是一个手误造成的错误:

make & make install。&位操作符导致的错误!应该是make && make install。

bash的位操作符有待深入研究,目前先把脚本跑起来再说。