Mage from hell

2014年4月20日星期日

从零学习Android开发

春节前突然发癫,想要自己做一个Android App,把以前Imagine Cup里面的Herbert游戏在手机上复制一份。这个游戏很难,但是实现应该还蛮简单的。虽说以前从来没接触过android开发,但是说干就干,我开始预计一周做完。

实际上从开始做research到最后完工整整我一个人用了一个月。我觉得这个应该不算慢了。回想起来,幸好我可以从以下几个渠道获得帮助:

  • Android Developer官方文档:相当完善,除了一些小地方不是最新的之外,有各种reference、tutorial、best practice,而且文档站是没有被墙的。基本涵盖了开发的方方面面。
  • StackOverflow:老生常谈了。作为新人,有好些问题根本不知道从什么地方入手,但是总是能从这里找到头绪。
  • Android Studio:基于IntelliJ IDEA的强大工具,非常好用,除了gradle时不时出个bug啥的。即使没有网络也有离线模式,Eclipse真的要被淘汰了。
  • 现役Android程序猿武智同学:哈哈,再好的文档也没有真人管用。有什么问题直接问靠谱的人比google还是快太多了。
虽然已经完成了一个app,但只能算是入门,android里面还有太多我不知道的门门道道。我猜我的代码在老手眼里肯定也是乱七八糟的,毕竟我基本没用过Java进行生产开发。离开自己的comfort zone是有点小成本的,这一个月我基本都是凌晨两点睡觉。

2012年11月4日星期日

Xen虚拟化的NAT和Bridge网络模式的共存

这是几个月前在下厨房做的一次改动。当时Xen虚拟化配置的是NAT模式,所有虚拟机均在一个局域网之中,和Dom0不在同一个网段之内。由于新增了一台物理机,我们就需要将新增的物理机和所有的虚拟机配置在同一个网络之内,这样就算以后再新增物理机也不会有任何麻烦。

我翻遍了Debian的wiki以及网上的所有帖子都没有找到如何在不重启Xend的情况下切换网络模式。Xen 4.x的网络部分相比3.x貌似有比较大的改动,官方文档已经不再推荐使用Xen自带的脚本配置网络,而是鼓励网管们根据自身的实际情况自行配置。Xen自带的脚本对于Bridge模式的配置还是很简单易懂的。我看了一下,相比3.x并没有什么变化。那么我们就自己来配置网桥吧。

我们的Dom0有四块网卡,公网IP在eth0上。那我们就把网桥和eth1关联起来。Dom0和新物理机都在192.168.0.x网段,所有虚拟机都在10.x.x.x网段。首先我们在Dom0上创建一个bridge:
sudo brctl addbr xenbr1

然后把eth1添加到xenbr1之中:
sudo brctl addif xenbr1 eth1

配置xenbr1的IP:
sudo ifconfig xenbr1 192.168.0.99/24

xenbr1就配好了,基本就是把Xen的脚本自己手动做一遍。然后就是配置所有虚拟机。为每台虚拟机都新增一块网卡,IP固定在Dom0的网段之内,也就是192.168.0.x。这包括在vm.cfg里面,把vif这一行注释掉:
#vif = [ 'ip=10.1.1.3,mac=00:16:3E:12:03:99' ]

然后改成:
vif = [ 'ip=10.1.1.3,mac=00:16:3E:12:03:99',
        'ip=192.168.0.3,mac=00:16:3E:12:03:9A,script=vif-bridge,bridge=xenbr1' ]


mac地址可以随便编,只要开头是00:16:3E,而且所有的虚拟机都不重复就行。虚拟机硬盘上的/etc/network/interfaces也要加入这块新的网卡:
auto eth1
iface eth1 inet static
 address 192.168.0.153
 gateway 192.168.0.1
 netmask 255.255.255.0


OK,大功告成。重启该虚拟机,它就会通过eth1和Dom0处于同一网段(192.168.0.x)上了。当然,如果你要完全去掉NAT,还是需要修改Dom0上Xen的网络配置并重启Xend才行。

2012年9月27日星期四

Mercurial Internal

最近在图灵社区上翻译了一篇mercurial架构的文章,顺便看了一下mercurial的代码和内部实现:http://www.ituring.com.cn/article/details/11708

我还没看AOSA这本书的git这一章。虽然平时git用的比较多,但是对git的内部实现还是认识的很模糊,没法和mercurial进行对比。就mercurial本身而言,除了在分支的实现上不如git之外,其他都非常好。mercurial一开始只能clone,这对于大型项目来说很坑爹。比如Firefox,开始克隆代码库之后程序员就可以去泡杯咖啡了。为了解决这个问题,mercurial后来加上了branch命令,可以原地切换分支。但是mercurial为了保持对svn和cvs用户的友好性,设计上还是希望在每个changeset之内记录分支名,不像git那么奔放,分支名就是个指针。

Mercurial的另一个特别之处在于每个changeset除了有一个hashid之外还有一个整数版本号。 虽然分布式版本控制系统中的版本历史是非线性的,但是这个整数版本号在任何一个mercurial版本库中都是线性的,只是顺序可能不同。这个数字版本号不仅使得mercurial对svn用户更友好,而且对于mercurial的内部实现很重要,它是mercurial快速读取版本历史的关键。

从社区的角度来说,Github完胜Bitbucket。我不知道为啥,可能是因为git比mercurial性能更好?或者Github更友好?我用python最多,所以也更偏爱mercurial。

Mercurial项目也有自己的性格,比如类名都是小写,变量名的单词之间不要下划线而是直接连起来写(比如loaddoc或者disabledext这样的函数名)。mercurial的代码中的注释真的多啊,不过注释多也不见得一定就是好事。给mercurial提交patch也是要带测试的,但是mecurial现在的测试已经太多了,跑起来要好久。我跑了一遍需要十多分钟。于是大家约定新的patch不再接受新的测试文件,但测试还是要,只是要塞在已有的某个测试脚本之中。囧!另外,虽然有个bugzilla来记录bug,但是所有的patch都要发到邮件列表里,有机器人会扫描新邮件,并根据邮件中的某些字样自动的在相应的bug中添加reference或者修改bug的状态。哎,真是奇怪的工作流程。

2012年3月9日星期五

自己实现 Interview Zen


偶尔遇到了www.interviewzen.com这个网站,一开始我觉得太神奇了,而且太酷了,这真是面试的神器!随即我就开始思考它是怎么实现的。

我并没有第一时间查看它的源代码。在考虑了几个小时之后,我感觉它可能是通过记录

2012年3月2日星期五

计算机编程语言的掌握程度

在简历上,“精通”这两个字基本上起到的都是嘲讽和增加仇恨的效果。一旦你自称“精通”某一门技术或者编程语言,面试官如果不是直接在心里给你打一个不靠谱的标签,那就会努力的证明你是不靠谱的——“精通”这两个字一般都会引来最最困难的提问。所以我一直在想,到底应该怎么形容自己的掌握程度呢?某种技术我说不好。但今天偶尔的一次谈话让我觉得对一门计算机编程语言的掌握程度,是可以有以下四个层次的:

1、知道
在这个层次,你已经了解了这门语言的基本语法。虽然这很微不足道,但也足够你阅读这门语言的程序了。不同语言在语法层面还是有一些区别的,比如语言的类型是编译还是解释、强类型还是弱类型、代码段是用begin/end还是{}表示、声明一个函数是用function还是def等等。

2、熟悉
在这个层次,你已经学会使用了这门语言的基本类库。只有语法没有类库的语言是没有意义的,熟悉这些类库是正确运用语言的基础。熟练使用操作文件系统、socket、日志、并发等等的类库,知道什么时候该用链表什么时候该用哈希,你才能算“熟悉”了这门语言。

3、掌握
在这个层次,你已经使用这门语言写出了一些能够解决实际问题的程序了。在前面的层次,你的代码解决的可能都是一些toy problem,但是只有你的程序经受了实践和时间的考验,你才能够有机会去解决一些书本上永远不可能出现的问题,并且更加深入的了解这门语言的细节。

4、精通
在这个层次,你已经阅读了这门语言的源代码。你应该已经了解它的内部实现,并且有了恍然大悟的感觉。


用这个角度检视自己,我对于各种语言的认知程度又有几分呢?

A类:最接近于精通的
Python:感谢《Python源代码剖析》这本书,给了我一个很好的入门。不过一来这本书我也没看完,二来Python本身的代码也在快速变化着,完全理解Python的实现仍然是一个很艰难的任务。

B类:掌握
C语言: 把C语言列在这里有点勉强,因为我上大学之后就基本没有写过C的代码了,不过C给我的影响仍然是巨大的,它塑造了我对计算机和程序的认识。
Javascript:Javascript是我很喜欢的一门语言,更不必说在这个浏览器的时代,它简直就是世界的未来。Douglas Crockford的那本《Javascript: The good part》是每个js程序员都必读的一本书,而Coffeescript则可以看作是这本书的实现。

C类:熟悉
C++、Java: 把这两门重量级的语言放在这一类实在是让我脸红,不过面向对象程序设计的确仍然是我最弱的地方。类、继承、多态、虚函数、接口——这些都不是问题,我似乎只是不知道怎么把这些螺丝刀派上用场。
PHP:玩具语言。
VB:感谢这门语言陪我度过了紧张的高中时光。

D类:知道
这一类就多了,不一一列举。


显然,随着时间的推移,这些分类也总是在变化的。有的语言因为你不再使用而变得生疏,有些则越来越熟悉。门户之见最没有意义,最重要的是把它们运用在正确的地方。

2011年1月7日星期五

为什么Latex的宋体可以加粗但不能倾斜?

这的确有点奇怪。不过,稍微做一下关于中文出版业的功课就可以知道了。英文中,斜体用在需要强调的地方,在Latex中对应为\emph{}。但在出版上,中文是没有“斜体”这一说的。需要强调的文字不应该用宋体,而应该用楷体。这也就是为什么Latex的CJK包就安装了宋体和楷体两种简体中文字体。

2010年12月17日星期五

此daemon非彼daemon

跳槽之后被各种琐事缠绕,好久都没有写文章了,今天匆匆记一笔

最近被daemon折磨了很久。原来一个程序要成为一个合格的daemon也是很不容易的,不光要进行两次fork,还有一大堆注意事项(PEP-3143):
  • Close all open file descriptors.
  • Change current working directory.
  • Reset the file access creation mask.
  • Run in the background.
  • Disassociate from process group.
  • Ignore terminal I/O signals.
  • Disassociate from control terminal.
  • Don't reacquire a control terminal.
  • Correctly handle the following circumstances:
    • Started by System V init process.
    • Daemon termination by SIGTERM signal.
    • Children generate SIGCLD signal.

偏偏我碰到的情况更特殊一点。这段代码的作者肯定是没看过上面这些条条框框的,把自己fork了两次就变身daemon了,然后在某些时候还用multiprocessing模块把自己再复制了几份来异步的做一些事情。在调试一个bug的时候,我注意到了multiprocessing 模块里讲 Process.daemon 属性时是这么说的:
The process’s daemon flag, a Boolean value. This must be set before start() is called.

The initial value is inherited from the creating process.

When a process exits, it attempts to terminate all of its daemonic child processes.

Note that a daemonic process is not allowed to create child processes. Otherwise a daemonic process would leave its children orphaned if it gets terminated when its parent process exits. Additionally, these are not Unix daemons or services, they are normal processes that will be terminated (and not joined) if non-daemonic processes have exited.


这实在是一段很难懂的话。就像伞兵生来就是要被包围的,守护进程daemon生来就是要被父进程抛弃的,然后再被init进程收养,沉默的完成自己的任务。但这段话却明确的说,当进程退出的时候,它会把自己的子daemon进程都干掉……那daemon还守护个毛啊?而且这里还说守护进程是不能创建子进程的,这更离奇了,太不公平了吧?这是赤果果的歧视啊。

后来我发现自己有点先入为主了。daemon只是一个概念而已,而非一个实际的标签,系统中的进程实际都是一样的。multiprocess模块的 Process.daemon 只是 Process 对象的一个属性而已,是需要开发者自己设置的而非linux帮你产生啊。理解了这一点,上面的这段说明就完全可以理解了。daemonic 类型的 Process 对象就是具有这些属性而已。这里的 daemon 和我们平常理解的 daemon 虽然有联系,但确是不同的了。

有个和我有相同困惑的人09年在python-list发了一封邮件问这个问题,后来又自己理解了,我也是看到他的邮件才领悟的。

* http://www.python.org/dev/peps/pep-3143/#correct-daemon-behaviour
* http://mail.python.org/pipermail/python-list/2009-May/1203871.html