博客

三环边上

California Hotel 很好听,它的歌词在英文很烂的我来看,也像迷一样,似懂非懂,但有一点很明显,他讲了一个故事。在我听了无数次 California Hotel 之后突然发现一个问题,中国的流行歌曲很少有叙事形式的歌,精确点说就是很少有从头到尾就是讲了一个故事的歌。都是抒情歌,抒情歌不是不好,可是总抒来抒去形式也太单一了,为什么大家不创作一些叙事歌呢?按说以前的孔雀东南飞什么的应该是有配曲的吧。当我发现这个问题时,我自己草草搜索了一下大脑,想来想去,只想起一首粤语歌分手总要在雨天

前几天看和菜头贴了一首家在东北三环边上,因为当时是 Ubuntu 环境,只看了歌词没听歌。今天又看到三表说这首歌,听了一下,这才注意到歌曲是改编二人转的(上次只看歌词时还真没发现 :$)。这是首典型的叙事歌,因此我又想起我几年前的那个问题了,这次再草草搜索了一下大脑,多了一首歌——东北人都是活雷锋

下面请用鼠标猛击 ▶ ,我地家在东北~~~~三环边上啊~~~~



附:有次看电视介绍,说 California Hotel 这是根据吸毒时的体验写出来的,网上相关链接也有很多。

在 PyS60 上使用 ctypes 修改 WIFI 的 MTU

自从换了 DELL 无线路由后,俺的 E61i 也出现无法正常连接 WLAN 上网的情况,而且和当初 Vista 的故障一样——可以访问 google,但大部分网站的连接不上,据以往经验,估计还是因为手机的 MTU 大于这个路由器最大支持的能力。

在网上搜了半天,发现在 S60 2nd 机型上,有一种改 genericnif.ini 的方法可以改变 MTU,但是到了 S60 3rd 机型,貌似就只能自己动手写程序了。

但 Symbian 下的 C++ 开发俺不熟啊,PyS60 没有接口啊,怎么办。正好在查相关开发信息的时候,发现 Symbian 出了一个 POSIX 兼容层(S60 平台上的称呼是 Open C),PyS60 社区有人据此移植了 libffi_ctypes 过去;又有人包装了兼容 Python2.5 的 ctypes 提供一些外围函数;wiki 上还有一个 open c 下执行 setsockopt 的例子

于是安装了 Open C 的 SDK, 然后在它的文档里找到这么一段:
The following flags will work only on hardware because it is supported by the underlying symbian API.

  • SIOCGIFMETRIC
  • SIOCGIFMTU
  • SIOCGIFNETMASK
  • SIOCGIFBRDADDR
  • SIOCGIFFLAGS
  • SIOCGIFDSTADDR
  • SIOCGIFPHYS
  • SIOCSIFMETRIC
  • SIOCSIFMTU
  • SIOCSIFFLAGS

只要确认 SIOCSIFMTU 是可以用的就好办了,对一个熟练的 C 程序员且有一定 ctypes 使用经验的人来说,除了平台移植问题外,开发是没有什么困难的。

开发过程如下

  1. 在手机上安装 libffi、Ctypes (_ctypes.pyd)、ctypes
  2. 在 Windows 下安装 S60 C++ SDK,以及 Open C plugin。可以找到 pips_nokia_1_3_SS.sis 等一共 4 个 .sis 文件,都安装到手机上
  3. 找到 winscw\udeb\libc.lib,然后用 dumpbin.exe /exports libc.lib 寻找函数名对应的序号,PyS60 的 ctypes 必须得用序号去定位函数,比如这样
    1. from _ctypes import *
    2. from ctypes import *
    3. libc = dlopen("libc.dll")
    4.  
    5. socket  = lambda x,y,z: call_function(dlsym(libc, "339"),   (x,y,z))
    6. ioctl   = lambda x,y,z: call_function(dlsym(libc, "180"),   (x,y,z))
    7. close   = lambda x:     call_function(dlsym(libc, "57"),    (x,))
    8. perror  = lambda x:     call_function(dlsym(libc, "255"),   (x,))
    9. dup     = lambda x:     call_function(dlsym(libc, "67"),    (x,))
    10. close(2)
    11. dup(1) #把标准错误重定向到标准输出,这样 perror 就输出到标准输出了
    12.  
    13. ntohl   = lambda x:     call_function(dlsym(libc, "168"),   (x,))
    14. ntohs   = lambda x:     call_function(dlsym(libc, "169"),   (x,))
    15. print ntohs(1), ntohl(256)  #简单的测试脚本
  4. 没有 dumpbin.exe 这个工具(VC++)的,也可以用 MinGW 的 nm.exe 来处理 libc.lib,只不过输出结果还需要再处理一下
    1. f = open("c:\libc.txt")
    2. idx = 1
    3. for i in f.readlines():
    4.     if i.startswith("00000000 T "):
    5.         if i[11] == '.':
    6.             continue
    7.         print idx, i[11:].strip()
    8.         idx += 1
  5. 根据 SDK 的头文件,找出 SIOCSIFMTU 这样的常量定义写成 python 语句
    1. IOCPARM_MASK = 0x1fff
    2. _IOC = lambda inout,g,n,l: (inout | (l & IOCPARM_MASK) << 16 | ord(g) << 8 | n)
    3. _IOR = lambda group, num, len: _IOC(0x40000000L, group, num, len)
    4. _IOW = lambda group, num, len: _IOC(0x80000000L, group, num, len)
    5. _IOWR = lambda group, num, len: _IOC(0x40000000L|0x80000000L, group, num, len)
    6.  
    7. SIOCGIFCONF     = _IOWR('s', 3, SIZEOF_IFCONF)
    8. SIOCGIFFLAGS    = _IOWR('i', 17, SIZEOF_IFREQ)
    9. SIOCGIFMTU      = _IOWR('i', 51, SIZEOF_IFREQ)
    10. SIOCSIFMTU      = _IOW('i', 52, SIZEOF_IFREQ)
    11.  
    12. IFF_UP          = 0x1
    13.  
    14. AF_INET         = 0x0800
    15. SOCK_DGRAM      = 2
    16. IPPROTO_UDP     = 17
  6. 还要去找 struct ifconf, struct ifreq 是怎样定义结构体的,结构体大小是多少
    1. SIZEOF_IFREQ = 84
    2. SIZEOF_IFCONF = 8
    3. IFREQ_IDXFMT = '52sH'
    4. IFREQ_MTUFMT = '52sI'
    5. IFREQ_FLAGSFMT = '52sBB'
    6. autopadding = lambda start, size: start + str(size - calcsize(start)) + 's'
    7. ifreq_idxfmt = autopadding(IFREQ_IDXFMT, SIZEOF_IFREQ)
    8. ifreq_mtufmt = autopadding(IFREQ_MTUFMT, SIZEOF_IFREQ)
    9. ifreq_flagsfmt = autopadding(IFREQ_FLAGSFMT, SIZEOF_IFREQ)
  7. 真正的操作很简单:
    1. sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
    2.  
    3. result = create_string_buffer(SIZEOF_IFREQ * 20)
    4. ifc = create_string_buffer(pack("II", SIZEOF_IFREQ * 20, addressof(result)), SIZEOF_IFCONF)
    5.  
    6. ret = ioctl(sockfd, SIOCGIFCONF, ifc)
    7. ifc_len, ifc_buf = unpack("II", ifc.raw)
    8. count = ifc_len / SIZEOF_IFREQ
    9. for i in range(0, ifc_len, SIZEOF_IFREQ):
    10.     ifreq = result.raw[i:i+SIZEOF_IFREQ]
    11.     ifr_name, ifr_idx, padding = unpack(ifreq_idxfmt, ifreq)
    12.     ifr = create_string_buffer(ifr_name, SIZEOF_IFREQ)
    13.     ret = ioctl(sockfd, SIOCGIFFLAGS, ifr)
    14.     ifr_name, ifr_flags, ifr_flagshigh, padding = unpack(ifreq_flagsfmt, ifr)
    15.     if not (ifr_flags & IFF_UP):
    16.         print "%s: DOWN" % ifr_name
    17.         continue
    18.     ret = ioctl(sockfd, SIOCGIFMTU, ifr)
    19.     ifr_name, ifr_mtu, padding = unpack(ifreq_mtufmt, ifr)
    20.     print "%s: UP, MTU: %d" % (ifr_name, ifr_mtu)
    21.     if ifr_mtu > 1400: #凡是 MTU > 1400 的,都改成 1400
    22.         ifr = create_string_buffer(pack(ifreq_mtufmt, ifr_name, 1400, padding), SIZEOF_IFREQ)
    23.         if -1 == ioctl(sockfd, SIOCSIFMTU, ifr): foo = perror("why")
  8. 但...为什么 perror 会报 Permission denied 呢? 后来查了半天,发现是 SIOCSIFMTU 需要有 NetworkControl 权限。于是去 这里 申请一个自己手机 IMEI 对应的证书(免费的),证书对应的 key 在这里
  9. ensymble 这个工具来给 libffi/Ctypes/PythonScriptShell(unsigned_testrange) 重新签名,加上 NetworkControl 权限
    ensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --execaps="ALL-TCB-ALLFILES-DRM" PythonScriptShell_1_4_4_3rdEd_unsigned_testrange.SIS imei_PythonScriptShell_1_4_4_3rdEd.sis

    ensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --dllcaps="ALL-TCB-ALLFILES-DRM" libffi.sisx imei_libffi.sis

    ensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --dllcaps="ALL-TCB-ALLFILES-DRM" ctypes.sisx imei_ctypes.sis

  10. 这下大功告成,我....我终于又可以在家里坐在马桶上用手机上无线了!!
Topic: 技术

关于牛奶

From blog 配图

十一期间亲戚们一起吃饭,收到一条彩信报,说是北京市决定给每头奶牛每天补贴伙食费15元。我把这条新闻当笑话讲给同桌的人听——这也太扯了,算下来每个月伙食补贴比搜狐午餐补贴还高——但正好我老婆的表哥曾经养过奶牛,他给我说:

奶牛的伙食是很好的,比如一头奶牛一天 50 斤奶的话,大概要吃 25 斤的粮食。光吃草,是产不出奶来的。

见到这里有一个奶牛专家,我一下来了兴趣,于是又从他那里了解到好多奶牛知识。

  1. 牛奶这条供应链上是三个环节,奶农、奶站、生产企业。
  2. 奶站以1.2元/斤的价格从奶农那里收奶,然后以1.3的价格卖给企业
  3. 奶站必然是要掺水的,氮化合物也随之添加
  4. 企业,或者说企业的原料采购部门对这个猫腻是很清楚的,据说曾有企业向奶站提出过以1.4的价格收购,前提是不要掺水,但奶站不干
  5. 假如收购价格从1.3提高到1.4还达不到掺水的利润的话,可见掺水的比例之高,或者说有毒物质比例之高
  6. 现在由于奶制品销量剧减,需求不足,最新的模式是奶站收了奶去卖,然后按卖的价钱计算给奶农的付款
  7. 养奶牛的成本是很高的,一头牛大概1w元。因为一年才一胎,就是说一只奶牛两年才能产一只新奶牛。奶牛也不是随时都有奶,还是在哺乳期内奶量大。
  8. 供应链还有一种优化的模式是"托牛所",就是养户出钱购买奶牛,集中交给奶站养,饲料什么的都由奶站负责

好啦,科普结束。

问题是,企业明知道它的供应商提供不合质量的产品,为什么长时间以来无法解决(我家一本十年前的书上就描述了如何检查牛奶中是否含有尿素)

我更关心的是:是不是在现有体系下,跨农业和工商业的(以及跨地域的)供应链就根本无法优化?比如"托牛所"这个东西,乳品企业能参股,或者是自己组建吗?县级/乡级政府在这里面的角色是什么?

如果供应链上游不能真正市场化,我觉得最后还是要出问题。

Topic: 商业 社会

我要抵制蒙牛了

本来我是一个很和谐的人,虽然平时有很不和谐很不主流的思想,但还不至于到网上发表右派言论或愤青言论。。。

但这次不做点什么也太对不起蒙牛了...

先抵制5年再说,不知道到 2013/9/28 ,这家企业还在不在

最后,马上就是共和国59年大庆。在生日之际像鲁迅那样说:"这孩子将来是要死的" 太恶毒。我的祝愿是:再坚持15年,就比苏联(1917—1991)更长寿了,hoho

Topic: 社会

给奥组委做的邮件系统容灾方案

据说一共有5家厂商提供了方案,俺代表搜狐提的方案最后得到采纳,日后要说起bj2008,我可以对达达说:当年你老爸也是为奥运建设出过一份力的...

现在残奥会也已经结束,按照当初商定的合同,搜狐的支持服务在一周后也就是9/30将正式终止,我想现在讲一下技术细节应该不会有什么影响了。

要解决的问题简单说就是:万一奥组委的邮件系统瘫痪,怎样才能让邮件系统尽快可用。

系统出问题有很多种,除了硬件故障、软件故障外,网络故障和机房故障也是需要考虑在内的。我根据搜狐邮件中心所能做的工作,提出的是一个异地容灾方案:

灾备系统在我们这里,把它设为低级别的 MX,平时不启动 25 端口,一旦奥组委确认需要启动该灾备系统,则启动 25 端口。将所有收到的邮件,比如说发给 foo@beijing2008.cn 的,利用 Milter 协议的 addRcpt 功能,增加一个 foo@vip.beijing2008.cn 的收件人。vip.beijing2008.cn 是本地 host 的一个邮件域,在紧急情况下,奥组委邮箱的原用户可以登录到这里查看并回复新收到的邮件。而发给 foo@beijing2008.cn 的邮件会在我们的 postfix 队列里等待,一旦奥组委的故障恢复,自然就能重新投递过去。

技术核心就是 Postfix 的 Milter 支持能力,以及针对 beijing2008.cn 的投递不是 DNS 查询而是 transport_table 指定。实施上最麻烦的是用户数据问题,和邮件没任何关系,却花了大量的时间在上面。

灾备准备好了,奥运期间也没有出现突发事件需要考验俺的这个方案。算是好消息,但也有点小遗憾。

最后要说的是,奥组委的IT部门,业务能力还是很不错的,比我以前做项目时接触的几个甲方单位强多了;还有一个首信的同仁,不知道是做项目监理还是负责整个的项目集成,几个电话打下来对他的敬业精神和认真态度也是很佩服。从小见大,奥运会本身怎么怎么样不去评论,但确实是把中国最优秀的人才集中在了一起去支持这个 big project。。。咳咳,上句所说的最优秀人才不包括我.

Topic: 技术

python 性能调优一则

最近几天新写的一个日志处理程序深受性能不足所困扰(每5分钟处理一次httpd日志,统计用户的连续访问和停留时间)。显然每5分钟处理过的数据必须至少保留 30 分钟后才能销毁,否则所谓连续访问无从算起。

问题是第一次处理的时候,程序很快就结束了,大概30秒;但第二次时间就增加到60秒左右,第三次时间增加到90秒上下...

很容易想到这是由于数据没有释放,导致在做查找操作的时候时间也随之延长...但我这里明明用的是 dict,应该是 o(1) 的复杂度,为什么这里表现出了线性增长关系呢??

代码很简单,我检查了又检查,函数封装了一个又一个(为了便于profile),从 hotshot 换到 cProfile,就是找不出程序慢在何处。

从 profile 结果看,最让人惊讶的就是主函数所调用的各个子函数的用时之和远远小于主函数所消耗时间(主函数内的计算量可以忽略不计),比如这样:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000  180.979  180.979 :1()
        1  114.522  114.522  180.979  180.979 test.py:33(main)
  5521410   32.147    0.000   32.147    0.000 {method 'split' of 'str' objects}
  5521409   16.107    0.000   24.152    0.000 test.py:7(process)
 11042818    6.586    0.000    6.586    0.000 {method 'has_key' of 'dict' objects}
  5521413    4.577    0.000    4.577    0.000 {method 'readline' of 'file' objects}
        3    3.594    1.198    3.602    1.201 test.py:20(commit)
  5521409    1.979    0.000    1.979    0.000 {method 'strip' of 'str' objects}
  3854644    1.459    0.000    1.459    0.000 {method 'append' of 'list' objects}
        3    0.000    0.000    0.008    0.003 {time.strptime}
        1    0.001    0.001    0.006    0.006 /usr/local/lib/python2.5/_strptime.py:12()
       10    0.000    0.000    0.004    0.000 /usr/local/lib/python2.5/re.py:186(compile)

总共 180 秒执行时间,但其它所有函数加起来也不超过 80 秒,另外的那 100 秒去哪里了呢?

然后就去翻 python 手册的 profile 部分,在看 timeit 的时候无意中扫过这么一段:
Note: By default, timeit() temporarily turns off garbage collection during the timing. The advantage of this approach is that it makes independent timings more comparable. This disadvantage is that GC may be an important component of the performance of the function being measured. If so, GC can be re-enabled as the first statement in the setup string.

我突然福至心灵,在代码里面禁止了自动垃圾收集,然后去测试,哈,问题立刻解决。不仅仅是丢掉的 100 秒找回来了,所有的操作速度都大大提高。原来分析5分钟数据要30秒的,现在10秒就搞定了!

这下就明白刚才的问题出在哪里了——在日志分析过程中创建出了大量的对象,而这些对象还都不能销毁,等待以后使用,结果就导致 python 频繁的 gc(陈儒的《Python源码剖析》里介绍是缺省每创建700个对象就执行一次 gc),而gc遍历队列是在线性增长的,这就表现为大量的时间被消耗掉,而且执行时间越来越长。

以前从来没有深入了解过 java,今天是第一次感受 gc 的作用 :PP

附:感觉还是 cProfile 比 hotshot 好,用 hotshot 很多系统内置函数的执行时间(比如字典的has_key, 字符串的 split)是看不到的。

Topic: 技术

写在 Chrome 发布时

我以前一直把“铬”读成 luo(四声),这回别人读 ge(四声),我才搞清楚 :$

不打算试用,先看看反响如何。不过,我个人觉得浏览器和提供网络服务的搅在一起,让人感觉不大安全。

为什么我用 Fx,除了对开源的东东抱有天生的好感之外,它是多 Tab 的浏览器,因此用 Fx 前,一直用 Maxthon。Fx 除了开源和多 Tab 外,还有一个非常重要的,也是让我持续使用它的原因——丰富的“扩展”。如果没有这些“扩展”,抛开开源的因素,从易用性来说,我怀疑 Fx 应该没有 Maxthon 好用。

在我看来,Fx 最重要的“扩展”是 Tab Mix Plus。没有它,我觉得 Fx 简直没法使用,这个“扩展”对我来说是缺之不可的。然后是 Adblock Plus,这个“扩展”属于如虎添翼型。再然后其他的“扩展”就多了,那些就是锦上添花了。

回到 Chrome,不知道它对于“扩展”(或者按照 Fx 的说法是“附加组件”)是如何考虑的,如果没有这种“扩展”,它真的可以对 Fx 构成威胁么?

更新:

今天报道太多了,看到了 Chrome 的一大堆介绍,其中有两个比较有特点的特点(:$话比较绕)

1. 多 Tab,但是每个 Tab 实际是一个进程。这个优势是某一 Tab 崩溃后不会影响别的 Tab,但是肯定会比传统的多 Tab 浏览器多消耗内存。

2. 可以把 mail(肯定还有 doc)单独建一个快捷方式在桌面上,当双击桌面快捷方式后,打开一个浏览器窗口,但是这个窗口没有地址栏,就像一个应用程序一样。呵呵,现在想想,其实当 Google 推 gears 以后,这一步就是必走的。如果 Google 真正能提供稳定的服务的话,什么 outlook,什么 office 都可以不要了,这种方式因为没有地址栏,表面上看就跟使用 outlook、office 一样,加上 gears 的存在,又可以离线使用,Google 真是阴啊。

Topic: 商业 网络

实践证明,水杯是笔记本第一大杀手

上一次笔记本损坏是5年多前了,那时候还是租房住,给宝宝洗澡的时候把水弄到键盘上了,好像就此彻底挂掉。

周六,不小心把水泼在了俺的 vostro 1000 上。立刻关机。等它干了以后,能正常启动,就是键盘上有若干键不能使用。

首先尝试自己动手,拆下键盘,把出问题的几个键都拔下来,拿着电吹风吹啊吹(这里要附带说一下,它的键盘是防水设计——就是把水尽量积在键盘上,而不是往主板上流;这样的设计还是很科学的),吹完以后问题照旧。

于是拿起电话打400,人家周末只有电话应答。只好周一再打电话,就说是键盘坏了,支持工程师问了问安全模式和BIOS设置模式下键盘是否仍然有同样问题,然后就决定给我换键盘。

很快今天上来工程师过来,把键盘换了,然后说12层还有另一个搜狐的员工笔记本报修就上楼了——汗...

这半年 vostro 1000 用下来,觉得还成。足够稳定,Vista 也运行挺好。除了硬盘温度比较高,让人怀疑如果在炎热的湖北使用它,就要天天开空调了

上一台 Thinkpad R51e 已经安然用了2年多,实践证明大厂出的廉价机种还是很可靠的,现在希望这台 DELL 也能坚持 3-4 年的寿命。

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