关于PHP Hashtable引起的bug的问题学习

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:

 
  1. <?php
  2. $size = pow(2, 15); // 可以通过第二个参数来改变提交的数据量的大小
  3. $data = ;
  4. for ($key=0, $maxkey=($size-1) * $size$key<= $maxkey$key+=$size) {
  5. $data .= $key.‘=&’;
  6.     $i++;
  7. }
  8. echo "key num: $i \n";
  9. echo "data length:"strlen($data);

通过这个脚本来得出key的多少和对应的数据量。

脚本2:

 
  1. <?php
  2. $size = pow(2, 15); // 16 is just an example, could also be 15 or 17
  3. $startTime = microtime(true);
  4. $array = array();
  5. for ($key = 0, $maxKey = ($size – 1) * $size$key <= $maxKey$key += $size) {
  6.         $array[$key] = 0;
  7. }
  8. $endTime = microtime(true);
  9. echo ‘Inserting ‘$size‘ evil elements took ‘$endTime – $startTime‘ seconds’"\n";
  10. $startTime = microtime(true);
  11. $array = array();
  12. for ($key = 0, $maxKey = $size – 1; $key <= $maxKey; ++$key) {
  13.         $array[$key] = 0;
  14. }
  15. $endTime = microtime(true);
  16. echo ‘Inserting ‘$size‘ good elements took ‘$endTime – $startTime‘ seconds’"\n";

通过这个脚本可以得出key的多少和相应消耗的时间。

80k的数据就可以构造8000 个key,耗费cpu时间大约不到2s;

360k的数据可以构造3.2万个key,耗费cpu时间大约30多s;

 

看来限制 100k 的数据还是非常有效的。

关于该bug的修复,官方给出了补丁,允许限制post数据的个数; 那么GET数据和Cookie数据会不会有这个问题呢?答案是不会的,我们可以从apache的源码中找到答案:

关于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  是用于遍历数组的一个位置指针

PHP post 数组初始化

PHP POST数组的初始化,下面的堆栈是我的PHP程序正在读POST的数据的时候打出来的:

# gstack 13339
#0  0x003aa7a2 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
#1  0x0048b12d in poll () from /lib/tls/libc.so.6
#2  0x00762558 in apr_wait_for_io_or_timeout ()
#3  0x0075bba4 in apr_socket_recv () from /data1/apache2/lib/libapr-1.so.0
#4  0x00e3d840 in socket_bucket_read ()
#5  0x08072668 in ap_core_input_filter ()
#6  0x00b21ad5 in logio_in_filter () from /data1/apache2/modules/mod_logio.so
#7  0x0807f634 in ap_http_filter ()
#8  0x013496e8 in php_apache_sapi_read_post ()
#9  0x0129399a in sapi_deactivate () from /data1/apache2/modules/libphp5.so
#10 0x0128cae6 in php_request_shutdown ()
#11 0x0134a1ca in php_handler () from /data1/apache2/modules/libphp5.so
#12 0x08073dea in ap_run_handler ()
#13 0x080741bf in ap_invoke_handler ()
#14 0x0807ee55 in ap_process_request ()
#15 0x0807c8c9 in ap_process_http_connection ()
#16 0x08079762 in ap_run_process_connection ()
#17 0x08082a6e in child_main ()
#18 0x08082c46 in make_child ()
#19 0x080837cd in ap_mpm_run ()
#20 0x08063de4 in main ()
———————–
# telnet 10.55.38.14 80
Trying 10.55.38.14…
Connected to 10.55.38.14 (10.55.38.14).
Escape character is ‘^]’.
POST /sso/login.php HTTP/1.1
host: login.sina.com.cn
content-length: 1000000000

aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaa

莫把重载当多态

偶一次,和一两个同事一起面试一个MM,鉴于MM几乎没有工作经验,但是至少是计算机专业的,我只想了解一下她的基础知识掌握的如何,我问了一下面向对象的三个基本特征;她回答道: 封装、继承和多态。

其实,封装、继承基本没什么不好理解的,但是多态却经常有人不理解。而且我也没想提问这个。我的一个同事A却提了个问题:PHP支持多态吗?PHP中的多态如何实现?

MM没能回答上来,我想,如果不是特别清楚的话,现在一定是要回答不上来的,因为面试官已经开始错了。同事A接着便自信地说:其实PHP不支持多态,但是实现起来却也很简单。我知道同事A错了,但是我对这个概念也有一些模糊了,便不想揭穿他,自己就默不作声,任由他们讨论。令我羞愧的是,另一同事B也和他们一起说起默认参数和几个参数的问题了,我彻底无语。不怕你不懂,就怕你错了还不知道自己错了。

多态不是重载,重载也不是覆盖;面向对象中,如果不能区分多态、重载、覆盖这几个概念,那么面向对象就是没有学好。

我们说:

  1.     我有一个函数move(animal),有一个参数是”动物“;如果你给的参数是老鹰,那么该函数表现出来的形态就是fly;如果你给的参数是小鸡,那么该函数表现出来的形态就是run; 这个叫多态(Polymorphism)。多态在C++中是通过虚函数来实现的,如果你是学C++的,你不了解虚函数,那么你一定不了解多态。
  2. 有多个可以接受不同个数、或类型的参数的同名函数,那么这个叫重载(  overload)。可以说,重载不属于面向对象领域(特有)的概念
  3.  如果子类定义了和父类完全相同的方法,那么这个叫覆盖
  4. 重载不是面向对象领域的概念

 

但愿我说明白了,参考文档:

  • http://baike.baidu.com/view/126521.htm
  • http://baike.baidu.com/view/126530.htm

繁华落尽,寂寞成殇

摘自:  http://www.lookmw.cn/yc/2820.html
  时光荏苒,岁月如梭。素什锦年,稍纵即逝,半载青春年华,似沙漏般,弹指间,流在昨天。苦涩与喜悦,都不再去回忆,依然埋在时光的烟尘里。只知青丝变白发,冥冥之中,注定青春与那寂寞有染。

    春去秋来,潮起潮落。往日里,天庭空旷,时光静谧;闲看云雾,静听风 雨;可谓悠闲自得,其乐无穷。转眼,又至寂寞时节,秋风抚叶,泛成黄色,落了一地的沧桑;鸟落廊前,轻声一吟,留下几世的凄凉。如花美眷,敌不过似水流 年。一切美好,都会悄然褪色,暗自凋零,最后落入尘埃。化作孤独,上了心头。

    繁华落尽,乱世成殇。人生似 列车,几经周转,穿行了多少过往。世人都是过客,到了青春驿站,转身何去何从。亲人,已挥手离去;朋友,也渐行渐远。风无定人无常,聚散两茫茫,留下寂寞 的你我,来来往往。看遍了人间繁华,城市的余辉,再美也终将落幕。远方飘来忧伤的夜曲,难得此生相逢,怎料寂寞如歌。待到繁华落尽,在这乱世,成殇。

    寂寞流年,染泪红颜。 灯火阑珊,最难将息。下玄月,西风凉,此夜何人眠;别时易,见时难,相思系红颜。弱水三千,只取一瓢;梦有万千,只梦一朝。衣带渐宽,为得伊人憔悴;千年等待,只求揽你入怀。泪如烈酒,灼人心肺,谁知相思,已成灾。

     往事成烟,宿命依旧。青春染指流年,流年染指红颜。画地为牢,锁我几春秋,染予寂寞。无奈拾起青春的记忆,等一阵清风扑面,挥挥衣袖,又是潇洒一片。

   

  注:

  非哲人,写不出满富哲理的文章

  非诗人,道不出流芳几世的佳句。

  只是简单平凡的我,肺腑之言,句句真切。

  谨以此文,

  祭奠即逝的青春,

  为自己的青春,

添上一段墓志铭

信手拈来

  1.     但求繁华落尽日,亦是得道成仙时.
  2.     楼中山外山,飞鸟各成单.万般皆下品,唯求常心安.
  3.     荒唐人间行, 处处透不平。不沾三分情,但求一身轻。
  4.     一宿黄梁梦,夜夜莫名痛.纵使千般好,万万不心动.
  5.     移山填海英雄气,难得世间女儿心.
  6.     命里有时终须有 命里无时莫强求。
  7.     三世交心。四世同寐。百年同船。千年共枕。
  8.     醉意易显风光,酒香莫说悲伤
  9.     ""前世的寄托,今生的陪伴.
  10.     月过梢,摇曳的花影, 空有一世情、
  11.                 鱼沈雁杳天涯路,始信人间别离苦。 
  12.             红颜远,相思苦,几番意,难相付。
  13.     疏影横斜水清浅、暗香浮动月黄昏。
  14.     当心中装满梦想 眼睛自然被打扫干净 前途无限大 因此别害怕 信念是一把火 燃烧起不灭的希望
  15.     嘚卟菿の詠逺恠騷動 被偏噯の嘟冇恃兂恐
  16.     风声萧瑟 余音渺渺 落叶怎知故人情忆往昔 虽不是峥嵘岁月 却还是忧愁
  17.     こ明月照出相思苦,诗词签名空余春风吹不归`
  18.     等闲变却故人心 却道故人心易变 –
  19.     谁啲眼泪触动了谁啲睸、谁啲笑容呧嘚了谁啲泪、谁啲訫载嘚下谁啲轮囬、谁啲掌纹赎嘚了谁啲嶵、
  20.     花 开 后 花 又 落,轮 回 也 没 结 果。
  21.     水因有性山难转,你若无心我便休
  22.     锦瑟年华,一曲拨断弦。惜流年,可曾相约天涯路踏遍
  23.     几度烟花落,繁华三千尽没
  24.     夕辞:谱夕辞,诉离伤,莫道容颜依旧,只是情思改。
  25.     等闲变却故人心,却道故人心易变。
  26.     梦中楼上月下 站着眉目依旧的你啊。
  27.     当时缠过红线千匝 一念之差为人作嫁。
  28.     い , 拔剑泪斩丝红尘,破而后立定皇图。
  29.     说的是一辈子,差一年,一个月,一天,一个时辰都不算一辈子…..
  30.     一片混乱之中,唯有蝶衣,独自与黑暗之中,传单之下,继续着未尽的绝美的舞步,丝毫未曾停滞…

TokyoTrant同步问题

1. 同样在一组磁盘上的两个TT端口,其读写操作是根据均匀的hash方式来分配的,但是,其中一个3001端口同步数据就很慢,而3002端口同步数据就很快,重启3001端口依然很慢; 把3002端口停掉之后,3001端口依然很慢。

2. 通过tcpdump抓包发现:
   3001端口的master 2001端口,数据流表现为:
      2001 ——–很多数据给—–》 3001
      3001 ——–因为吃不消,所以act 一个win(0) 给 —-》 2001
      2001 ——–不断询问 ———-》3001
      2001 ——–不断询问 ———-》3001
      2001 ——–不断询问 ———-》3001
      3001 ——–响应,说在给点儿吧 ——-》 2001
      2001 ——–很多数据给 ———…..
 
    使用netstat也可以发现,对于3001的同步连接中的接受队列总是会阻塞很多数据,看来是执行的太慢

3. 观察 3001端口的执行情况,下面是使用strace跟踪的这个繁忙的ttserver进程的系统调用情况:
  
  
   为什么futex耗费的时间如此惊人?

关于TokyoTrant的热备份

1. TokyoTrant(后面简称TT)在备份的时候不能提供写服务也不能提供读服务2. 备份的速度取决于磁盘的好坏

下面有一个TT备份的参考:
date; tcrmgr copy -port 3004 10.xx.x.xx @/data1/tokyotyrant/bin/ttbackup.sh; date
Thu Dec 22 23:22:59 CST 2011
Thu Dec 22 23:25:30 CST 2011

这里备份了12G的数据,用了2.5分钟;
磁盘性能:
5块 146G的磁盘做了逻辑卷的, 备份到同一个目录下, 如果按照300MB/s 的写速度, 300MB/s 的读速度,则每秒钟复制150MB的数据,则大约100s可以复制15G的数据;
而这里12G的数据复制了2.5分钟,看来我的计算是有些偏差的

另外一台机器的备份情况,备份数据量: 9.4G

关于复制时的一点注意事项:
当我们将复制好的从库启动之后,可能会发现延迟越来越大,可能是因为,数据都在磁盘上,虽然配置了很大的内存,现在还没能用上,磁盘的随机读写操作可能比较严重,过一段时间之后,随着磁盘数据越来越多地载入内存,这时候的读写操作都会很快了,延迟自然就越来越小了。

磁盘IO情况