博客

佩措尔德 G大调小步舞曲

《快乐的农夫》早就练好了,现在的目标是 Minuet in G major。

这首曲子听起来是相当的耳熟,以前一直传闻是巴赫的,后来考证出乃 Christian Petzold 的作品。现在右手已经很熟练了,左手的第一部分也很熟了。但在双手合练时碰到一个障碍——有一个小节是右手弹1/4音符,左手弹1/8音符——对右撇子来说,让左手弹得比右手快还保持节奏是挺不习惯的;而且紧跟这一小节后右手是一个波音左手有一个指位的变化,就更增加了偶这种菜鸟的心理畏惧感。

最近几个小时的练琴都是在攻克这个难关,争取本周能搞定,这个月把这首曲子练过去,08年结束之前再学一首新曲子

Topic: 音乐

关于 "#include"

我想写C程序都通常是把系统头文件放在最上面,然后是第三方库的,最后是我自己的,比如下面这样

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. #include <mysql.h>
  5.  
  6. #include <my_foo.h>
  7. ...

前天动手把我们的 apache module 移植到 nginx 上,里面自然是要 include 一堆 nginx 自己的头文件的。但它需要调 openssl 的库,那把 openssl 的头文件放哪里呢?我想了一下,觉得 openssl-devel 是系统自己 yum 安装上的,应该比 nginx 有更高的优先级,于是就是这么一个顺序

  1. #include <openssl/rsa.h>
  2. #include <openssl/pem.h>
  3.  
  4. #include <ngx_config.h>
  5. #include <ngx_core.h>
  6. #include <ngx_http.h>

(你看,我还是相当注重长幼尊卑的,正统思想啊~~)

这样程序似乎也没有问题,可就一个麻烦事情——调用 nginx 的 log 功能时就会 crash!用 gdb 跟踪,发现是使用 request->connection->log 的指针为空...

但就在 nginx 的代码里,向我的 handler 传入 request 之前,r->c->log 还是一个合法的指针,只要一进我的函数,这个就成了 NULL,而且一旦 handler 结束,指针值又非空了..

写到这里,聪明的读者就应该知道这是由于头文件预处理,导致两边结构体定义不一致。connection 这个结构体是如此定义的:

  1. struct ngx_connection_s {
  2. ...
  3. #if (NGX_SSL)
  4.     ngx_ssl_connection_t  *ssl;
  5. #endif
  6. ...
  7. };

就这么一个小小的 bug,足足花了2个小时才找到原因。

获得什么教训呢?

  1. 下回再用 gdb 跟踪到这种情况,应该能第一时间意识到结构体错误
  2. 如果要提供开发接口给第三方,那第三方可能直接使用的结构体里千万不要包括预处理代码
  3. 只要小心,是可以避免预处理的。但人总有大意的时候,那就养成一个好习惯,把预处理部分都放在结构体的最后,这就考验第三方开发者的人品了

最后是一篇新鲜热辣刚出炉的Nginx模块开发备忘:http://www.dup2.org/files/nginx_module_development.html
nginx 的中文 wiki 上有人写了一篇很好的翻译,我的备忘和其比起来,多了一些数据结构的分析。

Topic: 技术

发现 PyCrypto 不如 ctypes 来得方便

至少对于一个C程序员来说是这样..

有一段RSA签名的程序,原本是用 C 调 libcrypto 包装成 jni 由 java 使用的,现在打算在 python 里面实现一个类似的功能,这段 C 代码大概是这样:

int rsa_sign(char *private_keyfile, char *message, int message_len, char *buf, int buf_size)
{
RSA *key;
int malloc_size, rsa_size;

unsigned char *rsa;

BIO *bio;

bio = BIO_new_file(private_keyfile, "r");
if (NULL == bio) {
return 0;
}
key = (RSA *)PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
if (NULL == key) {
return 0;

}

malloc_size = RSA_size(key);
rsa = (unsigned char *)malloc(malloc_size);
if (NULL == rsa) {
RSA_free(key);
return 0;

}

if (0 == RSA_sign(NID_md5, message, message_len, rsa, &rsa_size, key)) {
free(rsa);
RSA_free(key);
return 0;
}

RSA_free(key);

if (rsa_size/3 * 4 >= buf_size) { /* 结果的 Base64 编码后长度大于 buf_size, 等于也错误(后面需要放 '\0' 的位置) */
free(rsa);
return 0;

}

b64_encode(rsa, rsa_size, buf);

free(rsa);
return 1;
}

可是找遍 PyCrypto, 楞是没有看到怎么实现 PEM_read_bio_RSAPrivateKey 这样从一个 PEM 格式的私钥文件里加载的功能。然后去看哪些基于 PyCrypto 包装的其他的包,比如 ezPyCrypto, Keyczar(这还是 google 发布的),包括 mail-list 上提供的 patch.... 这些似乎倒是可以 load key 了,可缺省好像也并没有对 NID_MD5 的支持, 绝望~~~~

于是抱着试试的心态用 ctypes 直接去调 libcrypto——以前只是在 PyS60 上玩票似的用过 ctypes,以及在 Win32 下用过,而且那时候 ctypes 还不太成熟——结果发现 python2.5 的 ctypes 竟如此的好用:

import ctypes

import base64

crypto_handler = ctypes.cdll.LoadLibrary("/usr/lib/libcrypto.so")
BIO_new_file = crypto_handler.BIO_new_file
PEM_read_bio_RSAPrivateKey = crypto_handler.PEM_read_bio_RSAPrivateKey
RSA_size = crypto_handler.RSA_size
RSA_sign = crypto_handler.RSA_sign
BIO_free = crypto_handler.BIO_free
RSA_free = crypto_handler.RSA_free

NID_md5 = 4

bio = BIO_new_file("/yourpathfile.key", "r")
key = PEM_read_bio_RSAPrivateKey(bio, 0, 0, 0)
r = BIO_free(bio)
if r != 1:
# break here

print 'BIO_free error'

rsa_size = RSA_size(key)
rsa = ctypes.create_string_buffer(rsa_size)

sign_size = ctypes.create_string_buffer(4)

message = "foobar"
if 1 != RSA_sign(NID_md5, message, len(message), rsa, sign_size, key):
print 'RSA_sign error'

RSA_free(key)

print base64.urlsafe_b64encode(rsa.raw)

Topic: 技术

Apache Module 之在各个 request 之间共享数据(资源)的方法

不论是1.3还是2.x,apache 的模式都包括一个进程服务好多次请求后再退出。现在有一个需求,在每个 request handler 里面希望保存一些数据,这些数据在以后该进程处理其他的 request 中可能还要用到。

最开始我从 request->server->process->pool 顺藤摸瓜看到一个貌似是和进程有关系的资源池。于是仿照 RUN_INIT_ONCE 的apr_pool_userdata_get/apr_pool_userdata_set 来访问资源。这样做的确可以在 request handler 之间共享数据,可是进程退出(apachectl stop)的时候不会去调 cleanup,非常之讨厌。

google 了半天,并参考 apache 自己的 LDAP 模块,发现官方解决方案好像是这样的:

  • 定义module_config结构体的时候,增加一个资源指针
  • 注册child_init的hook函数,从传入的第一个参数(pool指针)里面申请资源
  • 用apr_pool_cleanup_register注册cleanup函数在这个 pool 上(这个是cleanup的核心步骤,我感觉理论上完全可以自己重新创建一个pool,或哪怕是自己malloc,但关键是把cleanup函数注册到这个pool上。因为在进程退出的时候apache只会去销毁这个pool上的资源,而cleanup函数是"资源"之一)
  • ap_get_module_config, 把申请到的资源挂到自己定义的config结构体里

这样在进程退出的时候就自动 cleanup 了。这也是为什么到了 apache 2.0 后就不再提供 child_exit hook 的原因,因为开发者觉得由于有 cleanup 注册的机制,对应 child_init 的 child_exit 就不再有存在的必要。

唯一的疑问就是,request->server->process->pool 和在 child_init 时传入的 pool 是什么关系..... 不过懒得再去寻根究底了,先把需求实现了再说。


2008/11/25 UPDATE:上述的过程有一些问题,就是用 module_config 放资源指针,这样会导致虚拟主机的访问可能取不到 config。最简单粗暴的方法可能也是最有效的方法是用全局变量,当然别忘了 static 修饰。

Topic: 技术

取本机所有的 IP

代码修改自 qmail 的 ipme.c , 要点是对于 struct ifreq 里定义了 ifr_addr.sa_len 的系统(比如 FreeBSD),是和 Linux 不太一样的。
在 CentOS 3 和 FreeBSD 4.7 上测试通过

  1. /* ipme.c from qmail */
  2. #include "sys/types.h"
  3. #include "sys/ioctl.h"
  4. #include "sys/socket.h"
  5. #include "netinet/in.h"
  6. #include "net/if.h"
  7.  
  8. int main(int argc, char *argv[])
  9. {
  10.     struct ifconf ifc;
  11.     int sockfd;
  12.     char buf[20000];
  13.     char *ptr;
  14.  
  15.     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
  16.         return -1;
  17.     }
  18.  
  19.     ifc.ifc_buf = buf;
  20.     ifc.ifc_len = sizeof(buf);
  21.     if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
  22.         return -1;
  23.     }
  24.  
  25.     ptr = buf;
  26.     while (ptr < buf + ifc.ifc_len) {
  27.         struct ifreq *ifr;
  28.         struct sockaddr_in *sin;
  29.         int len;
  30.  
  31.         ifr = (struct ifreq *)ptr;
  32. #ifdef __FreeBSD__
  33.         len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
  34.  
  35.         if (ifr->ifr_addr.sa_family == AF_INET) {
  36.             printf("%s", ifr->ifr_name);
  37.             sin = (struct sockaddr_in *)&ifr->ifr_addr;
  38.             printf("\t%s", inet_ntoa(sin->sin_addr));
  39.             if (ioctl(sockfd, SIOCGIFFLAGS, ifr) == 0) {
  40.                 if (ifr->ifr_flags & IFF_UP) {
  41.                     printf("\tUP\n");
  42.                 } else {
  43.                     printf("\tDOWN\n");
  44.                 }
  45.             }
  46.         }
  47.  
  48.         // 这段代码来自 qmail 的 ipme.c, 在 FreeBSD 上实测中没有出现过 len 小的情况
  49.         if (len < sizeof(struct ifreq))
  50. #else
  51.         if (ioctl(sockfd, SIOCGIFFLAGS, ifr) == 0) {
  52.             if (ifr->ifr_flags & IFF_UP) {
  53.                 if (ioctl(sockfd, SIOCGIFADDR, ifr) == 0) {
  54.                     if (ifr->ifr_addr.sa_family == AF_INET) {
  55.                         sin = (struct sockaddr_in *)&ifr->ifr_addr;
  56.                         printf("%s\t%s\n", ifr->ifr_name, inet_ntoa(sin->sin_addr));
  57.                     }
  58.                 }
  59.             }
  60.         }
  61. #endif
  62.         len = sizeof(struct ifreq);
  63.         ptr += len;
  64.     }
  65. }
Topic: 技术

昨天终于把《快乐的农夫》的右手弹得像模像样了

"Joyous Farmer" Robert Schumann

左手好几个月前就能弹了,但右手怎么也不行... 后来挺忙的,就一直没有练这个曲子。

前天晚上练极简单的 Ode to Joy 以后,突然想着去把这首舒曼右手再练一下,没想到就很感觉很熟练了,:)

昨天反反复复弹到大拇指下某根筋隐隐作痛;今年的目标是把两手合起来...... 主要是缺练琴的时间,难啊~~~

自己弹没有感觉,看人家的演奏发现弹起来手的表演性还真强。

Topic: 生活 音乐

方刚的"三个容器"

见:http://fangzl.blog.sohu.com/102159550.html

昨天在一个小会上听方刚提到这三个容器,现在我觉得我可以帮着解读一下这三个代表的意思了....

第一个容器,是搜狐博客
第二个容器,搜狐空间... 搜狐空间只是起了一个 Space 的名

第三个容器,是....一张白纸. 用毛主席的话,就是好画最新最美的图画

核心是,这三个容器,在方刚看来,是...完全不兼容的。

但有总线把这三个容器串在一起...

但为什么方刚没有把privacy和 profile/relationship 归一处。而是和 APP 放在一个总线里面呢? 这个可得再问问他

Topic: 商业

dup2.org 恢复访问

由于原来的 vmmatrix 服务调整,现在把dup2.org切换到新的一个 VPS 上,感谢老韩友情赞助服务资源。

顺便把 drupal 也升级到了最新的 6.x

在从 5.x -> 6.x 的升级过程中,导出的 sql 文件有 1 个G!! 检查后发现都是 watchdog 这个表的内容。

watchdog 在 5.x 是核心模块还不能 disable,现在升级到 6.x,可以把"Database logging"这个模块禁用了,hoho

扩展模块现在只剩下 4 个: GeSHi Filter (满足时不时贴代码的需求)、XML Sitemap (搜索引擎优化)、CAPTCHA(对付垃圾留言)、Similar entries (提高网站的可阅读性). 别的东西我觉得都没有用,否则每次升级 drupal 都是一个麻烦事。

说实话,如果不是已经熟悉了 drupal 以及国外的 blog 服务容易被盾,真想切换到 blogspot 上. 要么使用 blog.sohu.com?? 好像很不严肃的样子...

文本聚类研究

最近一直在研究文本分类。到今天,终于搞出一个收敛的单词聚类算法。

最主要的时间是在磨刀上——前前后后总共差不多有 1 周的时间在看 sogou 分词库的文档,以及给它开发一个 python-binding

然后是差不多花了半天的时间写爬虫,获得最初的文档库

利用"[PDF] WRM :一种基于单词相关度的文档聚类新方法"里介绍的概念计算单词两两之间的相关度

最麻烦的就是利用相关度对单词聚类,论文里介绍的方法好像并不好用。从周二到周日,都在不停的找论文,试验新算法,运行测试,修正... 的循环里度过

失败无数次后,最后在 Fuzzy C-Means Clustering 的指导下,投机取巧的设计了一个方法以及判断收敛的机制,今天下午已经成功的跑完一次聚类到 250 个集合的过程,另一个聚类到 100 个集合的过程虽然比较慢,但在 2 个小时前也已经结束。Oh, my god, 看起来是可以正常工作了。

感谢分词库开发者,感谢搜狗新闻、关键字广告 TEAM 曾给的帮助

【尤其是 sogou 的开发包,能返回词的词性... 虽然还有瑕疵,但已经对我帮助很大了】

感谢python, 没有它不可能短时间内把算法体无完肤的改上10几次

感谢《鬼吹灯》,周末的晚上是它让我保持清醒,时不时的去看一眼后台跑的计算过程.

感谢家里人忍受我最近的加班

Topic: 技术

gsopcast 的 channel 列表

十一放假的某一天,突然想看看国际米兰的欧冠在线转播。发现一个网站http://www.myp2p.eu,里面有各种 P2P 软件的频道链接。鉴于我的 Ubuntu 系统,我看了半天,决定安装 SopCast,这个似乎还比较有名,网上的安装帮助应该比较多。

先安装了命令行版本,再加了图形界面 gsopcast。命令行的能用,不过比较麻烦。可图形界面里面,怎么也显示不出频道列表。于是我尝试先直接在浏览器里访问频道列表的网址,哦,原来已经被挡在外面了,难怪呢。

上周末把我的宽带换成 1M 的了,想起再试试 P2P 在线视频,顺便又搜了一下 gsopcast,发现有高人给了一个新的网址 http://channel.sopcast.com/gchlxml,好使。不过怎么我上次没有搜出来呢,这个网页还是隐藏得太深了,这个内容还是被人转得太少了,我也提一提,希望有同样问题的人能更容易地找到解决办法。

随便点了几个频道看,感觉速度不太好 :(

Topic: 生活 网络
订阅 RSS - 博客 | BT的花