这两天研究Nginx模块的实现机制,目的是写一个自己的module。调试过程中遇到一个诡异的问题,解决后发现是一个极小的失误,但这个’小臭虫’花掉我将近四个小时。
最终发现,问题起因于ngx_snprintf函数,这是Nginx自己实现的,类似C标准库中snprintf的一个函数(这是一个用户空间的函数 )。ngx_snprintf的原型为,
1 2 3 | //~ core/ngx_string.h u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...); |
我对这个函数的调用,
1 2 3 4 5 6 7 8 | u_char p[1024]; ngx_int_t len = ngx_snprintf(p, sizeof(p), "MySQL Server: %V:%u\n" "User: %V\n" "Password: %V\n", &rc_cf->db_srv, rc_cf->db_port, &rc_cf->db_user, &rc_cf->db_pass) - p; |
我们注意到,格式字符串中的%V,这是Nginx中特有的,用于输出Nginx中的字符串(一个struct,包含len和data两个域,不同于标准C中的NULL结尾字符串)。rc_cf->db_srv/db_user/db_pass都是这种类型,db_port是unsigned int类型。
问题就出在%u上面。我们知道标准C的printf家族的格式控制符中%u可以输出无符号整型,等同于%ui和%ud,即’i'或’d'是可选的。但在ngx_snprintf中,’i'和’d'是必选的,’u'只是起补充和修饰作用。于是乎,ngx_snprintf在看到%u之后会继续解析格式字符串(而不是开始对对应参数求值)。然后呢,然后栈就乱掉了。
问题确实很简单,调试并找出问题本也不应该太难。但遇到问题时,我首先想到的是自己对Nginx不是太熟悉,将注意力放在了周边环境,以为是对Nginx各模块的调用链不熟悉导致的。调试真是门学问,更多的是靠大量的代码经验和由此获得的面对问题的一种’直觉’。对,’直觉’,小工没有太多经验,需要很多规则来参照,专家却站在规则之上,靠直觉就可快速地定位、解决问题。
Nginx的代码很漂亮,代码量也只有10多万行,最核心的估计不到5万。但相对Apache,Nginx还是一个比较新的东西,相关的资源尤其是文档很匮乏。比如关于module开发这块,几乎仅有Emiller同志不算简短的一篇博客而已,能找到的两本书也只是Nginx的部署应用而已。祝愿Nginx日益强大,资源更加丰富。之后,我也会在学习过程中写点相关的博客,与大家分享,敬请期待。另外,如果你对Nginx有比较深的研究,或者有价值的资料,还请不吝相赠。
你好!除了代码,此处没有多少原创之物,皆为本人搜集、整理、总结之记录与心得,欢迎转载分享!转载时请尽量注明出处,将不胜感激。祝你健康、快乐!
Be the first to comment on this entry.