In Cisco routers, the codes for a traceroute command reply are:
1 | ! -- success |
In Cisco routers, the codes for a traceroute command reply are:
1 | ! -- success |
作者: Laruence( )
本文地址: http://www.laruence.com/2011/06/23/2057.html
转载请注明出处
其实一直想写这个系列, 但是一想到这个话题的宽泛性, 我就有点感觉无法组织.
今天我也不打算全部讲如何调试一个PHP的Core文件, 也不会介绍什么是Coredump, 选择一个相对比较简单的方向来介绍, 那就是如何从PHP的Core文件中获取一些对我们重演这个Core有帮助的信息.
在这个过程中, 会涉及到对PHP的函数调用, PHP的传参, PHP的一些全局变量的知识, 这些知识在我之前的文章中都有过涉及, 大家可以翻阅: 深入理解PHP原理之函数 深入理解PHP原理之变量作用域等等.
首先, 让我们生成一个供我们举例子的Core文件:
1 | <?php |
运行这个PHP文件:
1 | $ php test.php |
这个PHP因为无线递归, 会导致爆栈, 从而造成 segment fault而在PHP的当前工作目录产生Coredump文件(如果你的系统没有产生Coredump文件, 那请查询ulimit的相关设置).
1 | $ ulimit -c unlimited |
好, 现在, 让我们删除掉这个test.php, 忘掉上面的代码, 我们现在仅有的是这个Core文件, 任务是, 找出这个Core产生的原因, 以及发生时候的状态.
首先, 让我们用gdb打开这个core文件:
1 | $ gdb php -c core.31656 |
会看到很多的信息, 首先让我们注意这段:
1 | Core was generated by `php test.php'. |
他告诉我们Core发生的原因:”Segmentation fault”.
一般来说, 这种Core是最常见的, 解引用空指针, double free, 以及爆栈等等, 都会触发SIGSEGV, 继而默认的产生Coredump.
现在让我们看看Core发生时刻的堆栈:
1 | #0 execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:53 |
不停的按回车, 可以看到堆栈很深, 不停的是zend_do_fcall_common_helper_SPEC和execute的重复, 那么这基本就能断定是因为产生了无穷大的递归(不能一定说是无穷递归, 比如我之前文章中介绍深悉正则(pcre)最大回溯/递归限制). 从而造成爆栈产生的Core.
Ok, 那么现在让我们看看, Core发生在PHP的什么函数中, 在PHP中, 对于FCALL_* Opcode的handler来说, execute_data代表了当前函数调用的一个State, 这个State中包含了信息:
1 | (gdb)f 1 |
现在我们得到, 在调用的PHP函数是recurse, 这个函数定义在/home/laruence/test.php的第二行
经过重复验证几个frame, 我们可以看出, 一直是在重复调用这个PHP函数.
要注意的是, 为了介绍查看执行信息的原理, 我才采用原生的gdb的print来查看, 其实我们还可以使用PHP源代码中提供的.gdbinit(gdb命令编写脚本), 来简单的获取到上面的信息:
(gdb) source /home/laruence/package/php-5.2.14/.gdbinit
(gdb) zbacktrace
[0xbf400210] recurse() /home/laruence/test.php:3
[0xbf400440] recurse() /home/laruence/test.php:3
[0xbf400670] recurse() /home/laruence/test.php:3
[0xbf4008a0] recurse() /home/laruence/test.php:3
[0xbf400ad0] recurse() /home/laruence/test.php:3
[0xbf400d00] recurse() /home/laruence/test.php:3
[0xbf400f30] recurse() /home/laruence/test.php:3
[0xbf401160] recurse() /home/laruence/test.php:3
.....
关于.gdbinit, 是一段小小的脚本文件, 定义了一些方便我们去调试PHP的Core, 大家也可以用文本编辑器打开, 看看里面定义的一些快捷的命令, 一般来说, 我常用的有:
zbacktrace
print_ht**系列
zmemcheck
OK, 回归正题, 我们现在知道, 问题发生在/home/laruence/test.php的recurse函数的递归调用上了.
现在, 让我们来看看, 在调用这个函数的时候的参数是什么?
PHP的参数传递是依靠一个全局Stack来完成的, 也就是EG(argument_stack), EG在非多线程情况下就是executor_globals, 它保持了很多执行状态. 而argument_statck就是参数的传递栈, 保存着对应PHP函数调用层数相当的调用参数.
要注意的是, 这个PHP函数调用堆栈(层数)不和gdb所看到的backtrace简单的一一对应, 所以参数也不能直接和gdb的backtrace对应起来, 需要单独分析:
//先看看, 最后一次函数调用的参数数目是多少
(gdb) p (int )*(executor_globals->argument_stack->top_element - 2)
$13 = 1
//再看看, 最后一次函数调用的参数是什么
(gdb) p **(zval **)(executor_globals->argument_stack->top_element - 3)
$2 = {value = {lval = 22445, dval = 1.1089303420906779e-319, str = {val = 0x57ad <Address 0x57ad out of bounds>, len = 7}, ht = 0x57ad, obj = {handle = 22445, handlers = 0x7}},
refcount = 2, type = 1 '\001', is_ref = 0 '\0'}
好, 我们现在得到, 最后一次调用的参数是一个整数, 数值是22445
到了这一步, 我们就得到了这个Core发生的时刻的PHP层面的相关信息, 接下来, 就可以交给对应的PHP开发工程师来排查, 这个参数下, 可能造成的无穷大递归的原因, 从而修复这个问题..
后记: 调试PHP的Core是一个需要丰富经验的过程, 也许我今天介绍的这个例子太简单, 但是只要经常去挑战, 在遇到不懂的相关的知识的时候, 勇于去追根究底, 我相信大家终都可以成PHP Core杀手..
正则表达式,一个十分古老而又强大的文本处理工具,仅仅用一段非常简短的表达式语句,便能够快速实现一个非常复杂的业务逻辑。熟练地掌握正则表达式的话,能够使你的开发效率得到极大的提升。
正则表达式经常被用于字段或任意字符串的校验,如下面这段校验基本日期格式的JavaScript代码:
var reg = /^(\\d{1,4})(-|\\/)(\\d{1,2})\\2(\\d{1,2})$/;
var r = fieldValue.match(reg);
if(r==null)alert('Date format error!');
下面是技匠整理的,在前端开发中经常使用到的20个正则表达式。
1、校验密码强度 密码的强度必须是包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间。
^(?=.\d)(?=.[a-z])(?=.*[A-Z]).{8,10}$
2、校验中文 字符串仅能是中文。
^[\u4e00-\u9fa5]{0,}$
3、由数字、26个英文字母或下划线组成的字符串
^\w+$
4、校验E-Mail 地址 同密码一样,下面是E-mail地址合规性的正则检查语句。
[\w!#$%&’+/=?^_`{|}~-]+(?:\.[\w!#$%&’+/=?^_`{|}~-]+)@(?:[\w](?:[\w-][\w])?\.)+\w?
5、校验身份证号码
下面是身份证号码的正则校验。15 或 18位。
15位:
^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$
18位:
^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$
6、校验日期 “yyyy-mm-dd“ 格式的日期校验,已考虑平闰年。
^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$
7、校验金额 金额校验,精确到2位小数。
^[0-9]+(.[0-9]{2})?$
8、校验手机号 下面是国内 13、15、18开头的手机号正则表达式。
^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
9、判断IE的版本 IE目前还没被完全取代,很多页面还是需要做版本兼容,下面是IE版本检查的表达式。
^.MSIE 5-8?(?!.*Trident\/[5-9]\.0).$
10、校验IP-v4地址 IP4 正则语句。
\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b
11、校验IP-v6地址 IP6 正则语句。
(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))
12、检查URL的前缀 应用开发中很多时候需要区分请求是HTTPS还是HTTP,通过下面的表达式可以取出一个url的前缀然后再逻辑判断。
if (!s.match(/^[a-zA-Z]+:\/\//))
{
s = 'http://' + s;
}
13、提取URL链接 下面的这个表达式可以筛选出一段文本中的URL。
^(f|ht){1}(tp|tps):\/\/([\w-]+\.)+[\w-]+(\/[\w- ./?%&=]*)?
14、文件路径及扩展名校验 验证文件路径和扩展名
^([a-zA-Z]\:|\\)\\([^\\]+\\)[^\/:?”<>|]+\.txt(l)?$
15、提取Color Hex Codes 有时需要抽取网页中的颜色代码,可以使用下面的表达式。
\\#([a-fA-F]|[0-9]){3,6}
16、提取网页图片 假若你想提取网页中所有图片信息,可以利用下面的表达式。
\\< *[img][^\\>]*[src] *= *[\\"\\']{0,1}([^\\"\\'\\ >]*)
17、提取页面超链接 提取html中的超链接。
(<a\\s*(?!.*\\brel=)[^>]*)(href="https?://)((?!(?:(?:www\\.)?'.implode('|(?:www\\.)?', $follow_list).'))[^"]+)"((?!.*\\brel=)[^>]*)(?:[^>]*)>
18、精炼CSS 通过下面的表达式,可以搜索相同属性值的CSS,从而达到精炼代码的目的。
^\\s*[a-zA-Z\\-]+\\s*[:]{1}\\s[a-zA-Z0-9\\s.#]+[;]{1}
19、抽取注释 如果你需要移除HMTL中的注释,可以使用如下的表达式。
20、匹配HTML标签 通过下面的表达式可以匹配出HTML中的标签。
</?\w+((\s+\w+(\s=\s(?:”.?”|’.?’|[\^’”>\s]+))?)+\s|\s)/?>
下面是我找到的一张非常不错的正则表达式 Cheat Sheet,可以用来快速查找相关语法。 
我在网上看到了一篇相当不错的正则表达式快速学习指南,有兴趣继续深入学习的同学可以参考。
regex101是一个非常不错的正则表达式在线测试工具,你可以直接在线测试你的正则表达式哦。
ll -Sh *.mp4 | grep ^[^d] | awk '{print $5,$9;}'
在电商网站中,单页Web是非常常见的一种形式,比如首页、频道页、广告页等都属于单页应用。而这种页面是由模板+数据组成。传统的构建方式一般通过静态化实现。而这种方式的灵活性并不是很好,比如页面模板部分变更了需要重新全部生成。因此最好能有一种实现方式是可以实时动态渲染,以支持模板的多变性。另外也要考虑好如下几个问题:
整体架构
静态化页面的方案如下图所示:
直接将生成的静态页推送到相关服务器即可。使用这种方式要考虑文件操作的原子化问题(即从老版本切换到新版本如何做到文件操作原子化)。
而动态化方案的整体架构如下图所示,分为三大系统:CMS系统、控制系统和前端展示系统。
###CMS系统
1、在CMS系统可以配置页面的模板和数据;
1.1、模板动态在CMS系统中维护,即模板不是一个静态文件,而是存储在CMS中的一条数据,最终发布到“发布数据存储Redis”中,前端展示系统从Redis中获取该模板进行渲染,从而前端展示系统更换了模板也不需要重启,纯动态维护模板数据;
2、原始数据存储到“元数据存储Mysql”中即可,比如频道页一般需要:前端访问的URL、分类、轮播图、商品楼层数据等;这些数据按照相应的维度存储在CMS系统中;
3、提供发布到“发布数据存储Redis”的控制,将CMS系统中的原始数据和模板数据组装成聚合数据(JSON存储)同步到“发布数据存储Redis”,以便前端展示系统获取进行展示;此处提供三个发布按钮:正式版本、灰度版本和预发布版本。
目前存在如下几个问题:
1、用户如访问http://channel.jd.com/fashion.html怎么定位到对应的聚合数据呢? 我们可以在CMS元数据中定义URL作为KEY,如果没有URL,则使用ID作为KEY,或者自动生成一个URL。
2、多版本如何存储呢? 使用Redis的Hash结构存储即可,KEY为URL(比如http://channel.jd.com/fashion.html),字段按照维度存储:正式版本使用当前时间戳存储(这样前端系统可以根据时间戳排序然后获取最新的版本)、预发布版本使用“predeploy”作为字段,灰度版本使用“abVersion”作为字段即可,这样就区分开了多版本。
3、灰度版本如何控制呢?这个通过控制系统的开关来控制如何灰度;
4、如何访问预发布版本呢?比如在URL参数总带上predeploy=true,另外可以限定只有内网可以访问或者访问时带上访问密码,比如pwd=absdfedwqdqw。
5、模板变更的历史数据校验问题?比如模板变更了,但是使用历史数据渲染该模板会出现问题,即模板要兼容历史数据的;此处的方案不存在这个问题,因为每次存储时是当时的模板快照,即数据快照和模板快照推送到“发布数据存储Redis”中。
前端展示系统
1、获取当前URL,使用URL作为KEY首先从本机“发布数据存储Redis”获取数据;
2、如果没有数据或者异常则从主“发布数据存储Redis”获取;
3、如果主“发布数据存储Redis”也发生了异常,那么会直接调用CMS系统暴露的API直接从元数据存储Mysql中获取数据进行处理。
展示系统的伪代码
Java代码
--1、加载Lua模块库
local template = require("resty.template")
template.load = function(s) return s end
--2、动态获取模板
local myTemplate = "<html>{* title *}</html>"
--3、动态获取数据
local data = {title = "iphone6s"}
--4、渲染模板
local func = template.compile(myTemplate)
local content = func(data)
--5、通过ngx API输出内容
ngx.say(content)
即模板和数据都是动态获取的,然后使用动态获取的模板和数据进行渲染。
此处假设最新版本的模板或数据有问题怎么办?这个可以从流程上避免:1、首先进行预发布版本发布,测试人员验证没问题后;2、接着发布灰度版本,在灰度时自动去掉CDN功能(即不设置页面的缓存时间),发布验证OK;3、发布正式版本即可;正式版本发布的5分钟内是不设置页面缓存的,这样就可以防止发版时遇到问题,但是问题版本已经在CDN上给全部用户造成问题。当然这个流程很麻烦,可以按照自己的场景进行简化。
控制系统
控制系统用于版本降级和灰度发布的,当然可以把这个功能放在CMS系统中实现。
版本降级:假设当前线上的版本遇到问题了,想要快速切换回上一个版本,可以使用控制系统实现,选中其中一个历史版本然后通知给前端展示系统即可,使用URL和当前版本的字段即可,这样前端展示系统就可以自动切换到选中的那个版本;当问题修复后,再删除该降级配置即切换回最新版本。
灰度发布:在控制系统控制哪些URL需要灰度发布和灰度发布的比例,同版本降级类似将相关的数据推送到前端展示系统即可,当不想灰度发布时删除相关数据即可。
数据和模板动态化
我们将数据和模板都进行动态化存储,这样可以在CMS进行数据和模板的变更;实现了前端和后端开发人员的分离;前端开发人员进行CMS数据配置和模板开发,而后端开发人员只进行系统的维护。另外因为模板的动态化存储,每次发布新的模板不需要老重启前端展示系统,后端开发人员更好地得到了解放。
模板和数据可以是一对多的关系,即一个模板可以被多个数据使用。假设模板发生变更后,我们可以批量推送模板关联的数据,首先进行预发布版本的发布,测试人员进行验证,验证没问题即可发布正式版本。
多版本机制
我们将数据和模板分为多版本后,可以实现:
预发布版本:更容易让测试人员在实际环境进行验证;
灰度版本:只需要简单的开关控制,就可以进行A/B测试;
正式版本:存储多个历史正式版本,假设最新的正式版本出现问题,可以非常快速的切换回之前的版本。
异常问题
其中一个担心就是本机从“发布数据存储Redis”和主“发布数据存储Redis”都挂了,那么我们直接调用CMS系统暴露的HTTP服务直接从元数据存储Mysql获取数据。
另外一个担心是数据和模板获取到了,但是渲染模板出错了,比如遇到500、503;解决方案是:使用上一个版本的数据进行渲染。
另外还一种问题是数据和模板都没问题,但是因为一些疏忽,渲染出来的页面错乱了或者有些区域出现了空白;对于这种问题没有很好的解决方案;可以根据自己的场景定义异常扫描库,扫描当前版本有异常就发警告给相关人员,并自动降级到上一个版本。
来源:开涛的博客
mysql 5.5已经出来有一段时间,性能有明显提升,特别是对多核CPU的支持与TPS性能的提升。上周博主介绍了linux下编译安装mysql 5.5的步骤,安装不出意外基本没有问题。不过可能很多朋友和我一样一直用的是mysql 5.1,现在想把数据库升级成5.5了。博主根据实际操作,记录这次升级操作。
mysql基础信息
1、安装目录[root@vm-199~]# /usr/local/mysql
2、配置文件[root@vm-199~]# /etc/my.cnf
3、数据目录[root@vm-199~]# /data/mysql
4、启动脚本[root@vm-199~]# /etc/init.d/mysql
备份数据和安装、配置文件
[root@vm-199~]# mysqldump -uroot -p –all-databases </root/zhangnq/mysql5.1/mysql_dbk_20140217.sql
[root@vm-199~]# tar czvf mysql_5.1.60_full.tar.gz /usr/local/mysql
[root@vm-199~]# tar czvf mysql_5.1.60_data_full.tar.gz /data/mysql
[root@vm-199~]# cp /etc/my.cnf ./
数据备份好后关闭mysql数据库,/etc/init.d/mysql stop
,删除/usr/local/mysql文件。
安装mysql 5.5
具体可以参考这篇文章《Linux下编译安装Mysql-5.5的简单步骤》(http://www.sijitao.net/1563.html),安装目录、数据目录和5.1的一样,都是/usr/local/mysql 。
更新配置文件
[root@vm-199 mysql-5.5.35]# cp support-files/my-huge.cnf /etc/my.cnf
在配置文件中添加数据目录,datadir = /data/mysql 。
启动mysql 5.5,执行更新程序并重启mysql
[root@vm-199 mysql-5.5.35]# /etc/init.d/mysql start
[root@vm-199 mysql-5.5.35]# /usr/local/mysql/bin/mysql_upgrade
Looking for 'mysql' as: /usr/local/mysql/bin/mysql
Looking for 'mysqlcheck' as: /usr/local/mysql/bin/mysqlcheck
Running 'mysqlcheck' with connection arguments: '--port=3306' '--socket=/tmp/mysqld.sock'
Running 'mysqlcheck' with connection arguments: '--port=3306' '--socket=/tmp/mysqld.sock'
mydb.t1 OK
mydb.t2 OK
mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
mysql.help_topic OK
mysql.host OK
mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
mysql.servers OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
mysql.time_zone_name OK
mysql.time_zone_transition OK
mysql.time_zone_transition_type OK
mysql.user OK
Running 'mysql_fix_privilege_tables'...
OK
至此mysql已经更新好了。登陆mysql,检查数据是否和原来一样。
这个mysql升级其实不复杂,其实就是重新安装一遍,然后把数据目录文件覆盖一下。不过数据库升级,主要还是得注意数据备份,防止数据和意外丢失。
svn提交错误file is scheduled for addition, but is missing
svn delete
查找目录下的所有文件中是否含有某个字符串find .|xargs grep -ri "IBM"
查找目录下的所有文件中是否含有某个字符串,并且只打印出文件名find .|xargs grep -ri "IBM" -l
仅查找文件find . -type f|xargs grep -ri "IBM" -l
查找文件为0,包括空格处理find . -type f -size 0c -print0 |xargs -0 ls -l {}
删除为0的文件find . -type f -size 0c -print0 |xargs -0 ls -l {}
CentOS Linux Server 会将许多系统运行的状态发邮件给管理员root用户,系统邮件存放在 /var/spool/mail/root 目录中,可以通过使用 mail 命令来查看邮件,但是时间长了,也会造成邮件过多,如何使用 Shell 命令来批量删除或者全部删除呢?
方法一:使用mail命令,然后在 & 提示符下使用 d 命令(Delete),批量删除邮件,例如删除1~53邮件:
# mail
& d 1-53
方法二:删除全部系统邮件
# > /var/spool/mail/root or cat /dev/null > /var/spool/mail/root
或者
# echo “d *” |mail -N
有時候系統會提示收到郵件,通常是一些系統錯誤的通知,看完確認沒問題就可以刪了。
通知長得像這樣:
You have new mail in /var/spool/mail/root
刪除的方法很簡單,用以下兩種指令擇一即可。
cp /dev/null /var/spool/mail/root
/var/spool/mail/root
這樣就會清空root的mail。