slan's blog

有梦就去追,累了就休息

思想传播的重要性

思想传播的重要性

相比与绚丽的技术,思想就显得没那么引人注意,因为他不带来任何具体的革新。

回顾人类社会的发展,为什么近两百年的发展程度,远大于过去几万年的累积总和,甚至每年,每个月都有重大的技术,发明出来。

知识,与思想的累积与传播在此进程中占了非常大的作用。

在几千年前,知识与意识的累积,主要靠书简刻字,效率极其的低,社会的流动性也非常原始,到纸张的出现,与印刷术的发明,文明开始进入加速发展的时代,将人类拉入封建社会,出现一系列绚烂的文明,各种思想,技术开始交流,碰撞,社会开始有了流动性,出现了一些盛世文明。

然后到机械革命,蒸汽机的出现让人类开始了近代的高速发展,轮船,火车的出现让信息快速传播,世界开始成为一个整体,这直接导致了新的技术,能快速应用,快速改进,快速发明,举个飞机的例子,飞机也不是莱特兄弟突发奇想捣鼓出来了,整个世界同时有多个团队在研发飞机项目,汽油机的出现,空气动力学的发展让飞机出现成为可能,所以技术的累积与传播奠定了非常重要的基础。

项目会失败,人也会失败,但是思想不会,思想是世界上最有威力的武器,只要思想还在传播,累积到一定的量,一定会改变这个世界。所以现在世界上的独裁国家,无不以阻碍思想传播为第一大要务,一定要统一思想。

但是作为一个普通人,如何去改变世界?

做成一个改变世界的事情成功很难,以一己之力,或者一伙之力,去推动社会的变革也很难,执着于做这样的事情,大多数情况下都是一场空。

那么怎么才能为世界的发展贡献出自己的力量?

有两件事情,永远不会错,1,传播知识,思想。2,帮助传播知识,思想。

以程序开发为例。git,github为什么成功,它做了第二件事情,帮助传播知识,思想。

在git出现之前,你要去开发一款软件,如各种地方找第三方库,去遵守各式各样的规范,知识相对零散,共享程度较低。在git,github出现之后,约定了readme,约定了markdown文档,帮助知识组织起来,更方便的传播出去。极大的发展了开源运动。

同样,php的composer,把之前php相对零散的库组织了起来,更方便的安装管理,发展也极为迅猛。

传播知识,思想,是当前世界发展的主动力。经常写博客,写开源程序,传播社区精神,翻译,打破知识封锁,这都是我们能做的事情。一个人做的可能只是一件小事,可能被遗忘,被忽视。然而在整个人类的发展进程中,就汇聚了不可逆转的进步洪流。

这是一个坏的时代,这却是一个最好的时代,因为作为一个最最最普通的人,也能参与这个世界改变的进程。迟早,我们能累积到我们所期望的变革。

2014-12-20 18:43:00 +0000 UTC

无奈!gravatar被墙,博客头像打不开了

无奈!gravatar被墙,博客头像打不开了

事情是这样的,某天登录我的sae,突然提示,我的云豆只够用两天了,奇怪,我是sae高级开发者,每月有1.5w云豆,上面跑的云豆一般一个月就用几十个,根本用不完呀。

赶紧去看看,发现是自己写的一个没啥用的小玩意儿,一直废弃着,怎么一天居然耗了3000豆。。。

蓝线是这个应用的访问量pv,36w一天。。。。。。。看访问记录,都是一个啥监控的破网站

useragent:

xilin.sinaapp.com "GET HTTP/1.0" "-" "Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)" 69.64.56.47.1410600599359359 yq48

是这个网站搞的鬼 http://www.pingdom.com/ 跑去看了下,是个监控网站,在国外,于是开启防火墙,限制访问,一个ip访问5次就封掉。

效果很好:

访问一下就封死了。但是发现自己刷两下就不能访问了,这样不好,会影响别人访问,虽然也没人访问,但是处女座心理还是让我把限制放宽了。

放宽之后,云豆消耗立马又上来了,每天1000豆。一天十块钱啊,没几天就会用完了。

好吧9.4号-9.8号,一直消耗在1000云豆左右,于是放大杀招了,useragent中带pingdom,全部禁止访问:

if(strpos($_SERVER['HTTP_USER_AGENT'],'pingdom')!==false)
{
    die('fuck you!');
}

好了搞定,访问虽然每天仍然有40w,但是风轻轻云淡淡,一天就85个豆,毛毛雨拉。

2014-11-16 20:27:00 +0000 UTC

一次apache Segmentation fault (11) 崩溃问题定位

一次apache Segmentation fault (11) 崩溃问题定位

这些天出了一个很严重的问题。php接口,或者某个页面,会突然访问不了,查看apache错误日志,发现是不断的apache Segmentation fault (11),访问就会出现崩溃,之后所有的请求都会失败了。

之后运维重启apache解决,但是之后惨无人道的崩溃大幕开始了,每天晚上十点多的时候,某个站就会跑出来崩溃下。在分析最近更新内容后,发现没有明显能导致崩溃的更新或者写法。于是开发不行运维补,伟大的运维大神在连续几天晚上两三点被叫起来尿尿的情况下,终于想到了一个好办法,监控apache error日志的Segmentation fault (11) ,出现就重启,粗糙猛的办法临时解决了问题。

但是原因还是要找的,万一啥时候监控不准或者忘了加,或者掉了,那就出大事了。在这种找不到具体问题,也没法知道是更新了什么的情况下,只有分析apache的崩溃文件了。于是叫运维大神开启了apache崩溃产生core文件的配置。抓到了一堆core文件,gdb强势插入,bt观察,core文件大致分2种,

第一种崩在

崩溃1

remove_header,而且参数是 content-type ,大概估计是移除这个content-type的时候,崩溃了,看了下php代码,没有手动设置删除content-type,设置content-type倒很多,但都不是新增的,还是无法直接定位到具体的原因。

第二种是:

在销毁op_array的地方,完整堆栈: 崩溃2

注意下12-16层,是curl的调用,curl出了很多bug,于是猜测是新增了什么大量调用curl的功能,但是检查发现没有。

继续看类似几个core文件,发现除了curl,其他core文件还有mysql,file_get_contents等函数出现,然后就崩溃了,难道是阿里云网络最近不稳定,导致涉及网络调用的代码崩溃了?再仔细看看调用:

#7 0x004ea8ed in apr_pool_destroy (pool=0x9cbb920)

    at memory/unix/apr_pools.c:814

#8 0x001c06e5 in clean_child_exit (code=0) at prefork.c:218

#9 0x001c073d in just_die (sig=15) at prefork.c:344

#10

just_die (sig=15)应该是收到信号,然后apache退出,在释放资源的时候崩溃了,结合core文件生成的时候,能确定这种是产生Segmentation fault (11)后,监控脚本重启apache,然后apache进程在退出的时候,因为有网络调用的内容在跑,没处理好导致了崩溃,这种崩溃只在重启的时候才会发生,于是略过不管,真正致命的应该是第一种。

继续分析第一种core,完整堆栈信息

(gdb) bt

#0 sapi_remove_header (l=0x85e20e8, name=0xb7fc21a0 "Content-Type", len=12) at /usr/local/src/lamp/php-5.4.15/main/SAPI.c:601

#1 0x01426236 in sapi_header_add_op (op=, sapi_header=0xbff94460, tsrm_ls=0x85e2f60)

    at /usr/local/src/lamp/php-5.4.15/main/SAPI.c:650

#2 0x01426e8b in sapi_header_op (op=SAPI_HEADER_REPLACE, arg=0xbff944a0, tsrm_ls=0x85e2f60)

    at /usr/local/src/lamp/php-5.4.15/main/SAPI.c:842

#3 0x013b9415 in zif_header (ht=1, return_value=0xb7fc213c, return_value_ptr=0x0, this_ptr=0x0, return_value_used=0, tsrm_ls=0x85e2f60)

    at /usr/local/src/lamp/php-5.4.15/ext/standard/head.c:48

#4 0x014b21a4 in zend_do_fcall_common_helper_SPEC (execute_data=0xb7fb210c, tsrm_ls=0x85e2f60)

    at /usr/local/src/lamp/php-5.4.15/Zend/zend_vm_execute.h:643

#5 0x014b8d98 in execute (op_array=0xb7fb2070, tsrm_ls=0x85e2f60) at /usr/local/src/lamp/php-5.4.15/Zend/zend_vm_execute.h:410

#6 0x01480c7c in zend_execute_scripts (type=2, tsrm_ls=0x85e2f60, retval=0x0, file_count=1)

    at /usr/local/src/lamp/php-5.4.15/Zend/zend.c:1315

#7 0x015364fb in php_handler (r=0x8801058) at /usr/local/src/lamp/php-5.4.15/sapi/apache2handler/sapi_apache2.c:669

#8 0x0808541b in ap_run_handler (r=0x8801058) at config.c:168

#9 0x08088fbb in ap_invoke_handler (r=0x8801058) at config.c:432

#10 0x0809a707 in ap_process_async_request (r=0x8801058) at http_request.c:317

#11 0x0809a83d in ap_process_request (r=0x8801058) at http_request.c:363

#12 0x08097230 in ap_process_http_sync_connection (c=0x87f4b20) at http_core.c:190

#13 ap_process_http_connection (c=0x87f4b20) at http_core.c:231

#14 0x0808f32b in ap_run_process_connection (c=0x87f4b20) at connection.c:41

#15 0x00fb6c59 in child_main (child_num_arg=) at prefork.c:704

#16 0x00fb6ffd in make_child (s=0x8570d48, slot=15) at prefork.c:800

#17 0x00fb7b13 in prefork_run (_pconf=0x854c0a8, plog=0x85729a0, s=0x8570d48) at prefork.c:902

#18 0x0806ddc9 in ap_run_mpm (pconf=0x854c0a8, plog=0x85729a0, s=0x8570d48) at mpm_common.c:96

#19 0x0806886a in main (argc=139763872, argv=0x87f2940) at main.c:777

调用层比较少,说明还没跑什么就崩溃了。

这里有两个地方能看到php信息,第5层的 execute 以及第4层zend_do_fcall_common_helper_SPEC 。看下op_array中的信息:

找到了崩溃的php文件main.php,但是这是一个入口文件。基本所有功能都走这进入,要定位的范围还是很大。

继续看下zend_do_fcall_common_helper_SPEC,在gdb中,可以手动打印出函数的变量,可以查看下源码,看传递进去的变量是什么结构,然后构造个指针查看。

不过php调试有简单方法。php有个gdb调试脚本,可以较快的将php执行信息输出来,不用盯着c的结构体看细节内容,

来执行一下:source /usr/local/src/lamp/php-5.4.15/.gdbinit

然后切到zend_do_fcall_common_helper_SPEC的上下文中,命令是frame 层数 缩写是f,执行f 4

dump_bt类似bt,是php的gdb调试脚本提供的方法,能看到当前执行环境的调用层次,我们这就看到一层,在header设置content-type的时候就崩掉了。

main.php源代码如下:

date_default_timezone_set('Asia/Shanghai');
header('Content-Type: text/html;charset=utf-8');
define('APP_PATH',dirname(__FILE__)."/aae");//tita base dir
define('ROOT_PATH',dirname(__FILE__));
 
require APP_PATH . '/core/Core.php';//加载框架核心
 
$app=\core\Tita::App();
$app->run();

根据dump_bt提示,崩在16行设置header的地方,也就是这里的第二行,这比较头痛,刚到入口apache就崩了,应该不是php语句写错了导致的。这样子也不好绕过,继续往上看:

#0 sapi_remove_header (l=0x85e20e8, name=0xb7fc21a0 "Content-Type", len=12) at /usr/local/src/lamp/php-5.4.15/main/SAPI.c:601
#1 0x01426236 in sapi_header_add_op (op=, sapi_header=0xbff94460, tsrm_ls=0x85e2f60)
    at /usr/local/src/lamp/php-5.4.15/main/SAPI.c:650
#2 0x01426e8b in sapi_header_op (op=SAPI_HEADER_REPLACE, arg=0xbff944a0, tsrm_ls=0x85e2f60)
    at /usr/local/src/lamp/php-5.4.15/main/SAPI.c:842

最终是崩在remove_header,我们是设置header,为什么会remove header呢, 看源码。

sapi.c 601 行
/*
 * since zend_llist_del_element only remove one matched item once,
 * we should remove them by ourself
 */
static void sapi_remove_header(zend_llist *l, char *name, uint len) {
 sapi_header_struct *header;
 zend_llist_element *next;
 zend_llist_element *current=l->head;
 while (current) {
  header = (sapi_header_struct *)(current->data);
  next = current->next;
  if (header->header_len > len && header->header[len] == ':'
    && !strncasecmp(header->header, name, len)) {
   if (current->prev) {
    current->prev->next = next;
   } else {
    l->head = next;
   }
   if (next) {
    next->prev = current->prev;
   } else {
    l->tail = current->prev;
   }
   sapi_free_header(header);
   efree(current);
   --l->count;
  }
  current = next;
 }
}

这里对current这个结构体进行操作,l->head是个链表,在next = current->next;崩溃,当时的current是个已经损坏的zend_llist_element结构,导致继续遍历下个节点的时候崩溃了。注意这句:

zend_llist_element *current=l->head;

current最初是从l来的,幸好l还没损坏,

检查下链表指向

查看到第二个节点的时候,就发现异常了0x1d明显不像是个指针,也就是应该只有一个节点,第一个节点的next本来应该为null,这里却是一个不知道啥的值。在->next的时候就崩溃了

至此,大致情况清楚了:

我们的php程序在调用header('Content-Type: text/html;charset=utf-8');的时候,apache进入了sapi_remove_header,然后这个函数里,遍历header列表的时候,使用了一个未初始化或者已经释放或者不知道怎么被损坏的一个header链表。如何解决这个问题呢?既然我们不需要remove_header,那是不是不进入这里就可以了,再看下#1层的函数sapi_header_add_op,这应该是我们要操作的内容,设置header content-type

static void sapi_header_add_op(sapi_header_op_enum op, sapi_header_struct *sapi_header TSRMLS_DC)
{
 if (!sapi_module.header_handler ||
  (SAPI_HEADER_ADD & sapi_module.header_handler(sapi_header, op, &SG(sapi_headers) TSRMLS_CC))) {
  if (op == SAPI_HEADER_REPLACE) {
   char *colon_offset = strchr(sapi_header->header, ':');
   if (colon_offset) {
    char sav = *colon_offset;
    *colon_offset = 0;
          sapi_remove_header(&SG(sapi_headers).headers, sapi_header->header, strlen(sapi_header->header));
    *colon_offset = sav;
   }
  }
  zend_llist_add_element(&SG(sapi_headers).headers, (void *) sapi_header);
 } else {
  sapi_free_header(sapi_header);
 }
}

源代码显示op == SAPI_HEADER_REPLACE时才会进入sapi_remove_header,也就是我们传进来就是replace,看手册:

void header ( string $string [, bool $replace = true [, int $http_response_code ]] )

原来默认设置成replace,这个参数的意思是当有同名header的时候替换较旧设置的那个header,我们这因为是入口,代码保证了这个header是第一个,所以,直接设置成false,来避免进入崩溃的函数sapi_remove_header。改成:header('Content-Type: text/html;charset=utf-8',false);

虽然崩溃问题解决了,但是为啥链表会是个坏的,因为崩溃几率小,压测设置header也不崩溃,所以还无法复现。

2014-09-18 05:38:00 +0000 UTC

遭遇每天40w次攻击

遭遇每天40w次攻击

一个头像服务的网站,居然也被墙,真的很无奈

最近不知道抽什么风,墙越垒越高,sublime插件网站,stackflow这种跟敏感词毫无关系的站也经常访问不了。

前段时间,出于被墙的担心去阿里云备了案,准备把博客放到国内,却迟迟下不了决心。虽然迁回来可能看的人速度更快,更稳定了,但是自己却要付出额外的自由。且不说要挂上良民证,在备案的过程中,你必须说谎说自己做的不是博客,你必须关站,你必须上传你的照片。

出于对审查,以及随时因为被查到是博客而被取消备案的恐惧,以及心底的不情愿,备案这么久了还是没有迁到阿里云,国外虽慢,但是这地方也基本就我一个人在看吧。

有时候很愤怒,大多数时候是无奈。多希望环境越来越好,越来越自由,越来越开放,越来越尊重人。现实却是你得掌握各种莫名其妙的技能,比如翻墙。

现实越是难过,越是要心存美好,因为你要更加努力才能避开这艰难的世界,去欣赏那围不住的美好。

2014-09-13 17:46:00 +0000 UTC

you know nothing

you know nothing

当我抵触你时,你喜欢我。

当我熟悉你时,你深爱我。

当我开始喜欢你时,你因我而痛苦。

当我爱上你时,你却早已离我远去 。

我在此处悲伤,我在别地哭泣。

我悲伤地不是你离开了我。

我哭泣地不是你不再爱我。

我是绝望,

绝望这世上不再有一个人

像你般深爱懂我。

2014-06-13 21:06:00 +0000 UTC