redis持久化

redis持久化有两种方式,这里不详细介绍,我用的是默认方式,刚才重启服务器,发现没有载入持久文件,问题出在哪里呢?

出在配置文件上,配置文件指定的持久化dump.rdb文件,没指定路径,而安装的时候,是在安装目录直接启动redis服务的,所以持久化文件默认放在安装目录里,这次重启,rdb默认找到root目录了,没有找到rdb文件,当然就不会载入了,还好发现的早,修改配置文件,改成绝对路径,然后把安装目录下的rdb文件拷贝到指定目录,重启redis,一切恢复正常,中间丢了一点key,不过问题不大。

服务器问题很多啊

上午发现被当成采矿机的那台服务器,负载还是很高,cpu的wa非常高。

哪里出现问题了呢?

开始寻找吃cpu的几个进程,发现这么几个:

qmgr、bounce、error

都是什么玩意,查了一下原来根源在postfix,一个mail服务器,同时在/var/spool/mail/root里发现大量垃圾邮件。

原来被人当成垃圾邮件转发站了,各种受虐啊,果断停掉postfix。

这台服务器啊。。。

使用Joomla要当心

开源的东西是很好,不过盯着的贼也多。

一个备用的服务器,上面跑了个Joomla配置的网站,今天上去一看,load average竟然达到30多,top一下跑的进程,发现有4个minerd命令在跑,详细如下:

/minerd -a scrypt -o stratum+tcp://pool.litecoinrain.org:3333 -u Kel -p 1d9dcec911 -B

查了一下,竟然是比特币挖矿机,真是倒霉催啊,竟然被人种了个挖矿机,我竟然一点好处都没有。

看了一下运行的用户是www,还好不是root或者root权限的用户,一定是某个web有漏洞,find一下,果然发现是在Joomla站点下,tmp目录里,这个tmp目录是安装补丁插件的上传目录,之前有安装过模板,所以放开了写权限,果断修改权限,删掉minerd。

PHP_FUNCTION(addcslashes)、addslashes、stripcslashes、stripslashes

两对转义特殊字符的函数

addslashes经常用于防注入,注:mysql_real_escape_string要更安全。

看其中常用的一对吧,addslashes:

while (source < end) {
		switch (*source) {
			case '\0':
				*target++ = '\\';
				*target++ = '0';
				break;
			case '\'':
			case '\"':
			case '\\':
				*target++ = '\\';
				/* break is missing *intentionally* */
			default:
				*target++ = *source;
				break;
		}

		source++;
	}

这里有个特别的地方,当遇到\0时,显式的拼接了*target++ = ‘0’;而\’\”\\都是*target++ = *source;按理讲,这两种写法其实是一样的,那为什么\0要单独写个case且单独break呢?我猜\0本身就是字符结束的标记,拼接*target++ = ‘\\’;后,就无法再访问*target++了,所以不能使用*target++ = *source;只能显式拼接一个0

类似的stripslashes里也单独处理了\0

PHP_FUNCTION(similar_text)

proto int similar_text(string str1, string str2 [, float percent])
Calculates the similarity between two strings

计算两个字符串的相似度,完全一样,则返回100。

手册里说是依据这个算法:Programming Classics: Implementing the World’s Best Algorithms by Oliver (ISBN 0-131-00413-1)

没什么兴趣去研究这个算法,直接看源码吧:

sim = php_similar_char(t1, t1_len, t2, t2_len);

	if (ac > 2) {
		Z_DVAL_PP(percent) = sim * 200.0 / (t1_len + t2_len);
	}

直接调用了php_similar_char函数,返回结果显然是介于0~二分之一(t1_len+t2_len)之间,只有这样计算出来的percent才会介于0-100之间。

php_similar_char:

static int php_similar_char(const char *txt1, int len1, const char *txt2, int len2)
{
	int sum;
	int pos1 = 0, pos2 = 0, max;

	php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max);
	if ((sum = max)) {
		if (pos1 && pos2) {
			sum += php_similar_char(txt1, pos1,
									txt2, pos2);
		}
		if ((pos1 + max < len1) && (pos2 + max < len2)) {
			sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
									txt2 + pos2 + max, len2 - pos2 - max);
		}
	}

	return sum;
}

有递归,且又调用了php_similar_str函数:

static void php_similar_str(const char *txt1, int len1, const char *txt2, int len2, int *pos1, int *pos2, int *max)
{
	char *p, *q;
	char *end1 = (char *) txt1 + len1;
	char *end2 = (char *) txt2 + len2;
	int l;

	*max = 0;
	for (p = (char *) txt1; p < end1; p++) {
		for (q = (char *) txt2; q < end2; q++) {
			for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
			if (l > *max) {
				*max = l;
				*pos1 = p - txt1;
				*pos2 = q - txt2;
			}
		}
	}
}

看到了三层for,简单描述一下这三层for要干什么事情,对输入的两个字符串每个字符,都进行交叉比较,找出第一对不相等的字符,得到这对字符在第三层for遍历的步长,在这所有交叉比较中,找出步长最长的,赋给max,注意max一级pos1、pos2是引用,在php_similar_char里可以直接使用。最典型的是,如果两个字符串只有最后一对字符不相等,显然max=len-1,那么根据这个公式:sim * 200.0 / (t1_len + t2_len);会得到:

percent = (len-1)*200/(2*len)

len=1,percent=0;

len=2,percent=50;

len=3,percent=66.66;

…..

php_similar_char里还有递归,当不是第一对或者两字符串都没遍历完的时候,还要递归遍历下去,直到有一个遍历结束。

递归+三层遍历,这个函数要小心使用!!!!!

PHP_FUNCTION(strtr)

proto string strtr(string str, string from[, string to])
Translates characters in str using given translation tables

用给定的对照表,转换str中的字符。用了translates这个单词,我理解是替换,还是字符替换啊。。。

其实第二三参数也可以合并成一个关联数组。

看一下源代码:

if (ac == 2) {
		php_strtr_array(return_value, str, str_len, HASH_OF(*from));
	} else {
		convert_to_string_ex(from);

		ZVAL_STRINGL(return_value, str, str_len, 1);

		php_strtr(Z_STRVAL_P(return_value),
				  Z_STRLEN_P(return_value),
				  Z_STRVAL_PP(from),
				  to,
				  MIN(Z_STRLEN_PP(from),
				  to_len));
	}

两参数时,会调用php_strtr_array处理,三参数调用php_strtr处理,先看一下两参数的情况:

从php_strtr_array参数表里可以看出from是个hashtable类型,php_strtr_array里又分别调用了以下函数进行处理:

php_strtr_array_prepare_repls、php_strtr_array_prepare、php_strtr_array_do_repl

核心代码在php_strtr_array_do_repl里,说实话,这种命名很长有很类似的函数名,跳来跳去,很容易看晕头,最后两级遍历是少不了的,一级是遍历原串str本身,再遍历from,匹配到,执行拼接,将对应的to拼接到新串上,源码略。

回头再看三参数的情况,因为三参数执行的是char级的逐个替换,是逐个字符替换,所以代码就简单的多:

PHPAPI char *php_strtr(char *str, int len, char *str_from, char *str_to, int trlen)
{
	int i;
	unsigned char xlat[256];

	if ((trlen < 1) || (len < 1)) {
		return str;
	}

	for (i = 0; i < 256; xlat[i] = i, i++);

	for (i = 0; i < trlen; i++) {
		xlat[(unsigned char) str_from[i]] = str_to[i];
	}

	for (i = 0; i < len; i++) {
		str[i] = xlat[(unsigned char) str[i]];
	}

	return str;
}

源码写的很精巧,先定义一个char型数组xlat,初始化成key=value:for (i = 0; i < 256; xlat[i] = i, i++);

然后将from作为key,to作为value,重置xlat数组,这里要非常注意了,如果from里有重复的字符,因为for是从0开始循环,显然后面的对应关系会替换掉前面的对应关系。还有一点需要注意,最后一个参数trlen,这个长度取的是to串的长度,这里循环的长度用的就是trlen,所以from如果比to长,多余的字符是无效的,反过来呢,如果to比from长,没发现这里有什么特别处理,str_from[i],如果i越界了,会怎么样?

最后遍历一遍原串str,用xlat挨个替换,不在from里的那些xlat数组,其key是与value相等的,所以最后经过str[i] = xlat[(unsigned char) str[i]];处理的,只是在from里有的字符才会被替换。

php测试代码:

$str = strtr("abcdefg","abca","1234");
print_r($str);

 

PHP_FUNCTION(ucfirst)、lcfirst、ucwords

几个处理首字母大小写的函数

ucfirst:

toupper((unsigned char) *r);

lcfirst:

tolower((unsigned char) *r);

ucwords:

*r = toupper((unsigned char) *r);
	for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
		if (isspace((int) *(unsigned char *)r++)) {
			*r = toupper((unsigned char) *r);
		}
	}

如果当前字符(*r++)是空格,就把下一个字符转大写。特殊情况,如果连续空格,将后续空格转大写,那还是空格。

PHP_FUNCTION(quotemeta)

proto string quotemeta(string str)
Quotes meta characters

什么叫meta字符呢?解释起来费劲,直接看源码里,看哪些是mata字符吧:

for (p = old, q = str; p != old_end; p++) {
		c = *p;
		switch (c) {
			case '.':
			case '\\':
			case '+':
			case '*':
			case '?':
			case '[':
			case '^':
			case ']':
			case '$':
			case '(':
			case ')':
				*q++ = '\\';
				/* break is missing _intentionally_ */
			default:
				*q++ = c;
		}
	}
	*q = 0;

我的理解是正则匹配里有特殊含义的那些字符。

源码也就是上面这点,这句注释有意思break is missing _intentionally_,故意漏掉了break。这样的效果是:如果正好匹配到meta字符的话,在meta字符前加个反杠,然后拼接上meta字符,非meta字符,当然是直接到default里了。