看源代码要比看文档高效多了。文档是用自然语言写的,比如英语,日语,中文等等,文化的差异以及各种语法的不同使得文档理解起来很不方便,再说很多人根本就没有外语阅读的能力。自然语言还是相对比较复杂的,可是编程语言就简单多了,语法很简单,当前即使最高级的编程语言也还没有进化(如果它真的能进化的话)到足够复杂以至于必须学习额外的“文化”才能理解的地步,所有的编程语言都是简单的,因此用编程语言写就的源代码看起来就比理论文档易懂地多,前提是你要学习一些编程语言的语法哦!
经常听人说能看懂内核的人都是高手,其实并不是这样,看懂内核源码要比看懂一部历史著作容易多了,它都是流程化的,没有什么让你思考的东西,当然我指的仅仅是看懂而已,高手并不是看懂,而是彻底理解为何这么设计,归根结底还是人的思维在起作用,是设计者天才的大脑在起作用,背后是什么?背后还是文化,而文化的终极体现就是语言文字,自然语言文字,可以看出自然语言比编程语言高多少等级了吧。
很多人都认为源代码这种东西很难看懂,其实你只需要花顶多一个礼拜时间学点对应编程语言的语法,那么看懂局部就不成问题了,如果你再花一个礼拜学点操作上的知识,比如编译命令,调试步骤,在完全不理解这些原理的情况下你就可以看懂几乎全部代码了,所谓的难不在于代码本身难以理解,而是代码背后的设计难以理解,看linux内核源码很快,但是学习操作系统设计理论却很难,后者可能是用自然语言写成的一厚本书,可能混杂一些数学语言,可能没有一句编程语言,可能你要学十年--还不一定学的怎样!我们花了从不到1岁到x岁这么长的时间来学习说话,阅读和写作,我们现在拿起一篇文档肯定会说它比源代码简单,实际上可能我们根本没有花一分钟在编程语言的语法学习上。
以一个例子结束本文。第一次使用rsyslogd的人可能都会遇到一个问题,那就是在快速记录日志的情况下,日志会丢失,也就是记录不完全,这是怎么回事呢?第一步当然是用ps找出rsyslogd对应的进程号,然后使用lsof确认它打开了/dev/log这个本地套接字,然后使用strace看看它此时在干什么,注意加上-F -f参数以防它派生子进程...或者使用rsyslog -d(debug)重新运行rsyslog,此时使用loger命令快速写几条日志(摘自《Linux Security Cookbook》9.28):
#!/bin/sh
PROG=`basename "$0"`
FACILITIES='auth authpriv cron daemon ftp kern lpr mail news syslog user uucp
local0 local1 local2 local3 local4 local5 local6 local7'
PRIORITIES='emerg alert crit err warning notice info debug'
for f in $FACILITIES
do
for p in $PRIORITIES
do
logger -p $f.$p "$PROG[$$]: testing $f.$p"
done
done
只要strace输出一个rsyslog的信息或者rsyslog -d输出一个信息,此时就在rsyslog源代码中顺着找下去,然后的做法有好几种,可以打印调试,也可以gdb断点调试...也可以只看代码先不调试--在打印debug信息附近寻找蛛丝马迹,终于发现了下面的逻辑,在action.c的actionWriteToAction中:
if(pAction->iSecsExecOnceInterval > 0 && pAction->iSecsExecOnceInterval + pAction->tLastExec > getActNow(pAction)) {
DBGPRINTF("action not yet ready again to be executed, onceInterval %d, tCurr %d, tNext %d/n",
(int) pAction->iSecsExecOnceInterval, (int) getActNow(pAction),
(int) (pAction->iSecsExecOnceInterval + pAction->tLastExec));
pAction->tLastExec = getActNow(pAction); /* re-init time flags */
FINALIZE;
}
pAction->tLastExec = getActNow(pAction); /* re-init time flags */
pAction->f_time = pAction->f_pMsg->ttGenTime;
什么也不用说了,就是它引起的了,那么此时我们看一下iSecsExecOnceInterval这个值是什么时候被给上值的,发现是iActExecOnceInterval赋值给iSecsExecOnceInterval的,进一步发现iActExecOnceInterval是一个配置参数:
CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyonceeveryinterval", 0, eCmdHdlrInt, NULL, &iActExecOnceInterval, NULL));
于是我们只需要在配置文件rsyslog.conf中增加一行:$ActionExecOnlyOnceEveryInterval 0
经常听人说能看懂内核的人都是高手,其实并不是这样,看懂内核源码要比看懂一部历史著作容易多了,它都是流程化的,没有什么让你思考的东西,当然我指的仅仅是看懂而已,高手并不是看懂,而是彻底理解为何这么设计,归根结底还是人的思维在起作用,是设计者天才的大脑在起作用,背后是什么?背后还是文化,而文化的终极体现就是语言文字,自然语言文字,可以看出自然语言比编程语言高多少等级了吧。
很多人都认为源代码这种东西很难看懂,其实你只需要花顶多一个礼拜时间学点对应编程语言的语法,那么看懂局部就不成问题了,如果你再花一个礼拜学点操作上的知识,比如编译命令,调试步骤,在完全不理解这些原理的情况下你就可以看懂几乎全部代码了,所谓的难不在于代码本身难以理解,而是代码背后的设计难以理解,看linux内核源码很快,但是学习操作系统设计理论却很难,后者可能是用自然语言写成的一厚本书,可能混杂一些数学语言,可能没有一句编程语言,可能你要学十年--还不一定学的怎样!我们花了从不到1岁到x岁这么长的时间来学习说话,阅读和写作,我们现在拿起一篇文档肯定会说它比源代码简单,实际上可能我们根本没有花一分钟在编程语言的语法学习上。
以一个例子结束本文。第一次使用rsyslogd的人可能都会遇到一个问题,那就是在快速记录日志的情况下,日志会丢失,也就是记录不完全,这是怎么回事呢?第一步当然是用ps找出rsyslogd对应的进程号,然后使用lsof确认它打开了/dev/log这个本地套接字,然后使用strace看看它此时在干什么,注意加上-F -f参数以防它派生子进程...或者使用rsyslog -d(debug)重新运行rsyslog,此时使用loger命令快速写几条日志(摘自《Linux Security Cookbook》9.28):
#!/bin/sh
PROG=`basename "$0"`
FACILITIES='auth authpriv cron daemon ftp kern lpr mail news syslog user uucp
local0 local1 local2 local3 local4 local5 local6 local7'
PRIORITIES='emerg alert crit err warning notice info debug'
for f in $FACILITIES
do
for p in $PRIORITIES
do
logger -p $f.$p "$PROG[$$]: testing $f.$p"
done
done
只要strace输出一个rsyslog的信息或者rsyslog -d输出一个信息,此时就在rsyslog源代码中顺着找下去,然后的做法有好几种,可以打印调试,也可以gdb断点调试...也可以只看代码先不调试--在打印debug信息附近寻找蛛丝马迹,终于发现了下面的逻辑,在action.c的actionWriteToAction中:
if(pAction->iSecsExecOnceInterval > 0 && pAction->iSecsExecOnceInterval + pAction->tLastExec > getActNow(pAction)) {
DBGPRINTF("action not yet ready again to be executed, onceInterval %d, tCurr %d, tNext %d/n",
(int) pAction->iSecsExecOnceInterval, (int) getActNow(pAction),
(int) (pAction->iSecsExecOnceInterval + pAction->tLastExec));
pAction->tLastExec = getActNow(pAction); /* re-init time flags */
FINALIZE;
}
pAction->tLastExec = getActNow(pAction); /* re-init time flags */
pAction->f_time = pAction->f_pMsg->ttGenTime;
什么也不用说了,就是它引起的了,那么此时我们看一下iSecsExecOnceInterval这个值是什么时候被给上值的,发现是iActExecOnceInterval赋值给iSecsExecOnceInterval的,进一步发现iActExecOnceInterval是一个配置参数:
CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyonceeveryinterval", 0, eCmdHdlrInt, NULL, &iActExecOnceInterval, NULL));
于是我们只需要在配置文件rsyslog.conf中增加一行:$ActionExecOnlyOnceEveryInterval 0
就可以了,最后确认一下就是这样。这件事从发现这个问题算起到解决不到5分钟,如果看文档的话,我想5分钟是解决不了的,文件毕竟是人看的,学习用的,出了问题最好的解决办法就是调试+看源码,看不了源码的看堆栈,看字符串...总之要用计算机的方式去解决之,要知道,机器的问题要比人的问题更简单。
本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271162