星期三, 十月 31, 2007

Linux 内存寻址 1GB(896MB) 限制问题

《Understanding the LINUX KERNEL》(《深入理解LINUX内核》)一书,P51 提到:
In theory, up to 4GB of RAM could be installed on such systems; in practice, due to the linear address space requirements of User Mode processes, the kernel cannot directly address more than 1GB of RAM, as we will see in the later secion "Paging in Linux."


然后,在 P70 中(Paging in Linux -> Kernel Page Tables)中首先说明的是"Final kernel Page Table when RAM size is less than 896MB"。这个 896MB 根据后面的说明,是 1GB - 128MB = 896MB,128MB 有别的用处。但这个 1GB 是怎么来的呢?

参考 P47 关于 "Regular Paging" 的说明,可以看到,实际上每个进程(Process)都有它自己的 Linear Address 空间,默认会分配一个 Page Directory 和 Page Table(因此最小内存占用量为 8KB),而在 "Paging in Linux" 中讲到,系统被分为 User Mode 和 Kernel Mode,如果将 kernel 看作一个进程(init),那么系统最多为它预留的内存只能是 1GB,否则它将不得不使用 User Mode 的内存作为内核之用。因为 User Mode 无法获得直接获得 Kernel Mode 的内容,而 Kernel Mode 则可以从 User Mode 中存取数据,所以以最通常的情况 4GB 最大寻址空间为例,则 3GB 作为 User Mode 使用,1GB 给 Kernel Mode 使用,而 Kernel Mode 的线性地址空间为:0xc0000000 到 0xffffffff。

这个 4GB 的最大寻址空间是指 32 位使用最简单的线性地址表示方法时可以映射的最大寻址空间,也就是只有一个 Page Directory 和 1024 个 Page Table 的情况,此时是一个两级的目录状态。为了支持 Intel 处理器的 PAE(Physical Address Extension)功能以支持超过 4GB 的物理内存,Linux 可以在 PD(称为 Page Gloabl Directory, PGD) 和 PT 之间增加 Page Upper Directory 和 Page Middle Directory,显然这增加了目录级数,也就意味着增加了寻址时间。所以如果不使用那么大的内存,Linux 会将 PUD 和 PMD 的长度设定为 0 并直接映射为 PGD 的一个条目。

但这个 Kernel Mode 和 Low Memory/High Memory 有联系吗?

"Permission Denied" when load php module (SELinux)

Cannot load /usr/local/apache2/modules/libphp4.so into server: /usr/local/apache2/modules/libphp4.so : cannot restore segment prot after reloc: Permission Denied
这个错误是因为打开了 RedHat 的 SELinux,关闭就可以了。

另外,在编译 php 时使用了 --enable-gd-native-ttf 和 --with-gd=/usr/local 参数,待同样报告类似的错误:
/usr/local/lib/libgd.so.2: cannot restore segment prot after reloc: Permission Denied
,同样也是 SELinux 造成的。

星期六, 十月 27, 2007

"Progmatic Unit Testing", 心得和自省

昨天花了一天时间,把《Progmatic Unit Testing》这本书基本上过了一遍。写的很不错,结合之前做项目时写单元测试的经验,有一些心得,也必须做一些反省。

最主要的一个方面是关于测试代码本身的质量。测试代码应该与产品代码有同样的质量,因此要遵循 DRY 和正交性、低耦合的设计原则,而这一点在我之前的测试中做的很不好,以 Python Tree 来说,本来我想测试应该保证逻辑上的尽可能简单,因此为了保证测试覆盖面足够,结果包含了很多重复的语句,尤其是有时侯需要对一个变量(Tree Node)的几个方面做检查的时候,这些检查语句都要反复写,这就很不好。

在书中提到,最好从 TestCase 继承一个类,然后所有的其他所有的测试类都从这个继承基类再继承,这样可以在这个基类里面做 setUp() 和 tearDown(),并且可以自定义 assert/fail 函数。例如,对 Python Tree,我大可以这样定义:
class TestTree(unittest.TestCase):
def assertValidTreeNode(node, value):
self.failUnlessEqual(node(), value)
...

class TestSetAttr(TestTree):
def testNotExisted():
self.root.branch = 1
self.assertValidTreeNode(self.root.branch, 1)
...
在 cutils 的 mirrord/fs_mirror 项目中也存在这个问题。这样导致我在单元测试上花费了太多的时间,特别是如果对产品代码做出改动,在单元测试中就需要改很多地方,也就是说,复用性不好!不够专业。

另一个重要的问题是”独立性“不好。这一点在做 cutils 的 mirrord/fs_mirror 的时候尤其明显。一个方面是对于环境的依赖,在 mirrord/fs_mirror 中,因为并发非常重要,所以在 test_mirrord 中需要测试这种不同的并发情况会产生的不同的结果,之前的做法就是调度一个实际的实例,然后进行一些 sleep/wait 来等待并发的状态变化。但因为并发状态会如何变化是不受控制的,在不同的主机上结果也可能完全不同,所以只能等的更久来保证状态一定会变化(实际上有时侯也难以完全保证),结果就是运行的时间很长。另外,这也导致了在编写的时候更难保持 DRY 和正交性、低耦合的原则,对进度很不利。

我当时还在找多线程/并发的单元测试方法,好像对于 Java 还有专门的这样的软件。但现在看来,其实并不需要,只要利用 Mock 对象,模拟出相同的接口,然后在里面可控的设置并发状态,这个问题应该很好解决。

在 caxes Tree 的顺序问题也反映了独立性不够,主要是必须保证以前的节点不会因为后面的操作而受到影响,之前的做法就是逐一检查,当然也就导致了重复,而且后面的操作受前面的影响,没有前面的操作,后面就无从谈起,但完全可以利用 fixture 做多个 TestSuite,每次使用 fixture 和 test case 的 setUp() 重建就好了,然后利用 fixture 和 test suites 重新调用前面的测试就可以了。

在 test_mirrord 也有顺序问题,因为并发状态不同,可能结果不同,那么测试 server thread 的时候和测试 monitor 的时候结果可能不同!我之前的做法显然是错误的,只是因为想不到好的办法,就将前面测试 monitor 的结果记录到文件中,在 测试 server thread 的时候调出来比较,这显然不正确,只不过一般机器的运行调度结果在一般情况下会一样,所以基本上不会出现 fail 而已。

关于测试的“彻底性”和"自动化"方面,应该做得还可以,不过因为复用性不好,比较烦琐。当然,书中提到的对测试覆盖和边界条件的检查的原则还是相当有价值的,我之前也很难说做的很好。

另外,使用数据文件的技巧也很有意思。

要重写了! '')

meizu miniplayer 死机

上次买了一个,除了听音乐之外,主要也是为了作为一种存储和学英语的工具。前天挂到 LFS 下面的时候,发现分区表全是乱的,当然挂不上来。没多想,mkfs 了一下,结果再起来就不行了,不再创建默认的目录了。

在 Windows 下格了几次,每次插入都提示要重新格式话,试了一下 fat 格式,结果就没办法再启动了,每次都死机!

搜了一下。发现可以长按"Enter"键关机,但再启动还是死机。但在"长"按"Power + >>"键后可以自己格式化,而不需要进入 Windows,屏幕提示 Formating...,不过必须在刚开机的时进行这个操作,否则死机后就不行了。再启动后发现可以了。

参考:
http://bbs.imp3.net/thread-384771-1-6.html

官方资料:
http://www.meizu.com/support/faq_bbs.asp?Type=2

不知道是不是低格,因为其中说到"Power + <<"是低格。

用 WinXP 再格成 Fat32,结果在进入 LFS 还是分区乱。原来没有在 Linux 内核里面编译 vfat 模块!看来忙得实在是太晕头了。

星期四, 十月 25, 2007

用 bc 做进制转换

sh$ echo "obase=16; ibase=10; 1024 * 4096" | bc -l
400000
obase 即 output base,ibase 即 input base,结果当然是 0x400000

星期二, 十月 23, 2007

Ian Murdock 见面会

周一晚上去参见了 CU 组织的 Debian 创始人 Ian Murdork 见面会,不过 Ian 现在在 Sun 工作,自然也少不了 Sun 公司的广告啦。不过因为人比较多,Ian 减少了演讲的时间,留了一个半小时给大家提问,这本来是件好事情,可惜主办组织者太不地道了。

我本来有一些技术上的问题想问问看,虽说口语不怎么样,还是准备了一下,至少写在纸上了,可惜举手一个半小时,也抢不到话筒,因为话筒根本就不是由主讲人 Ian 来点名的,而是主办方在下面"偷偷摸摸”递来递去,除非你什么也不管,站起来就说逼得主办方不得不把话筒递给你,否则话筒基本上只会递给他们熟脸的人。好像大家都在乎那本提问就发的书似的。结果常常搞得 Ian 还没搞清楚人在哪里,眼睛还看着东边,声音就从西边响起来了,真是一点礼貌都没有。

问的问题也是一些泛泛而谈,没有什么实质性的内容。什么“Sun 的战略”呀,什么“为什么 Solaris 要关注个人用户”“向 Linux 靠拢”呀,什么“Microsoft 也搞开源啦,那么开源运动的未来怎样”呀,什么“IBM 也有 openJDK,那么对 Sun 有什么冲突”呀,什么“为什么 xx 编程比赛没有 Java"呀,什么“开源对国防安全的意义”啦等等等等...简直是在浪费大家的时间嘛。又不是搞新闻发布会。

最后,没办法,只能在最后结束后,等那些签名、拍照的人都排完了,过去说了两句,递了一张纸条,把在做的项目和个人邮箱写在上面,并说希望他能够看一下我正在做的开源项目,给我一个 feedback 作为指导和建议,他看了一下,看出了其中那个 file system mirror 的项目,并说 OK。说起来,这好像还是我真正第一次与外国人对话吧。

当然,我不能指望一定会有回复,最终要走下去,只能靠自己。

路漫漫其修远兮,吾将上下而求索。
荃不察余之衷情兮,反信馋而其怒。
亦予心之所善兮,虽九死其尤未悔。

星期五, 十月 19, 2007

Linux zip compression

在 Linux 使用 zip 打包一个目录:
sh# find dir/ | zip dir.zip -i -@

星期二, 十月 16, 2007

google sitemap

今天花了点时间了解了一下 google sitemap_gen,并在项目的文档站点上部署了一下,主要是为了使其更利于搜索以促进项目的推广。

首先从 sourceforge 上下载 sitemap_gen,解开后,拷贝 example_config.xml 为 config.xml,拷贝 example_urllist.txt 为 ulfs_urllist.txt,编辑这两个文件。config.xml 文件我只保留了 URL 和 URLLIST 两种方法,指定 base_url 和 store_into 到站点根目录下的 sitemap.xml.gz 文件,这样 google 可以通过 web 访问到这个 sitemap 文件,urllist 指向到 ulfs_urllist.txt:
http://crablfs.sourceforge.net
http://crablfs.sourceforge.net/index.html lastmod=2007-10-14T22:48:00+01:00 changefreq=monthly priority=1.0
http://crablfs.sourceforge.net/sysadm_zh_CN.html lastmod=2007-09-25T22:17+01:00 changefreq=monthly priority=0.3
http://crablfs.sourceforge.net/tree.html lastmod=2007-07-05T03:32:00+01:00 changefreq=monthly priority=0.5
http://crablfs.sourceforge.net/ru_data_man_zh_CN.html lastmod=2007-10-15T01:14:00+01:00 changefreq=weekly priority=0.6
然后上传 3 个文件:config.xml, ulfs_urllist.txt 和 sitemap_gen.py 到站点根目录下。

接着使用 ssh 登录到站点,运行 sitemap_gen.py 命令:
[chowroc@pr-shellC htdocs]$ python sitemap_gen.py --config=config.xml
Reading configuration file: config.xml
Opened URLLIST file: ulfs_urllist.txt
[WARNING] Discarded URL for not starting with the base_url: http://crablfs.sourceforge.net
Sorting and normalizing collected URLs.
Writing Sitemap file "/home/groups/c/cr/crablfs/htdocs/sitemap.xml.gz" with 4 URLs
Notifying search engines.
Notifying: www.google.com
[WARNING] Cannot contact: www.google.com
Count of file extensions on URLs:
4 .html
Number of errors: 0
Number of warnings: 2
去掉 ulfs_urllist.txt 中的第一行,在运行:
[chowroc@pr-shellC htdocs]$ python sitemap_gen.py --config=config.xml
Reading configuration file: config.xml
Opened URLLIST file: ulfs_urllist.txt
Sorting and normalizing collected URLs.
Writing Sitemap file "/home/groups/c/cr/crablfs/htdocs/sitemap.xml.gz" with 4 URLs
Notifying search engines.
Notifying: www.google.com
[WARNING] Cannot contact: www.google.com
Count of file extensions on URLs:
4 .html
Number of errors: 0
Number of warnings: 1
仍然报告无法 contact www.google.com,那么可以登录到 google 的 Webmaster tools,手工添加 sitemap,在 "Sitemaps" 中提交:
http://crablfs.sourceforge.net/sitemap.xml.gz
等待一段时间之后,可以看到已经正确提交的显示。

关于 google sitemap_gen 的使用文档在:
https://www.google.com/webmasters/tools/docs/en/sitemap-generator.html

星期六, 十月 13, 2007

bind slave 同步中的一个问题

昨天发现辅 DNS 同步有问题,一些后来加入的域名没有被传输,重启主、辅服务器都没有用,但在 /var/log/messages 里面也没有显示报错信息。

主、辅服务器都是使用的数据 zone 文件,于是我在辅助服务器上删除了原来的数据文件,再重启辅助服务器,发现这次新的域名被同步过来了!

星期五, 十月 12, 2007

python *args and wrapped BDB's pop() default

>>> def f(x, **kwargs):
... print kwargs
...
>>> f(1)
{}
我在 cutils 项目的 mirrord/fs_mirror 中好几个地方都使用了 Berkeley DB,并且是包裹在一个模拟字典类的对象中的(hash table like),为使其 pop() 操作更接近于内置的 dict 对象,特别是在返回默认值的操作上能够保持一致的行为方式,编码如下:
def pop(self, key, *args):
if args:
try:
args_len = len(args)
if args_len > 1:
raise TypeError, "pop() takes exactly 2 arguments (%d given)" % args_len
default = args[0]
except KeyError:
pass
try:
self.dbfile.pop(key)
except KeyError, kexc:
try:
return default
except NameError:
raise KeyError, kexc

python iterator 的一种用法

>>> d = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
>>> it = iter(d)
>>> for x in it: print x
...
a
c
b
d

开始使用百度的空间

以前的文章都帖在 google 的 blogspot 上面了,但我总是缺乏安全感(总之我活在这个现世里面就没有真正感到过安全),担心有一天如果 google 的帐号出问题访问不了了,那数据的损失就大了!

有一些备份的方法。对于 gmail,一般的邮件客户端没办法很好的备份,因为不在 Inbox 中的邮件无法下载,而且象 labels 这些东西也没有办法了;通过 filter forward 转到另一个邮箱的办法也不是太好,因为转过去不好分类,会很乱,而且对于同一个 session 的保存也不是很好,并且以前老的邮件也没办法处理。虽然有一个 libgmail 的 Python 模块,但是我当时使用了一下它的 demos/archive.py,效果不是太好,中间会退出,可能是中文的关系,而且它的界面也不是太好,交互终端的方式不是很灵活,我想有时间再研究它的基本原理和代码,然后自己写一个,可以直接提交 gmail 的查询指令就好了,然后在一个默认的交互式命令行里面操作——如果可能,再在上面包装终端和 GUI。

不过目前来说,没有时间,只能是用 filter forward 的办法了。但是对于其他的服务就不好办了。比如 blog——如果有一个程序能够一键搞定所有与这个帐号相关的数据的备份就好了,如果能够实现增量以及差分备份则更佳,不过目前我也没有发现这些东西,而且我现在实在也没有时间去做这些事情。所以对于以前的 blogspot,只能用一种土办法来备份了。一个备份脚本如下:

#!/bin/sh

day=`date -I`
backdir=/mnt/file/internet/google/$day
if ! [ -e $backdir ]; then
mkdir -p $backdir
elif ! [ -d $backdir ]; then
echo "Not a directory" >&2
exit 1
fi

wget http://chowroc.blogspot.com/search?max-results=999 -O $backdir/blog_all_public_posts.html
wget http://chowroc.blogspot.com/feeds/posts/default?max-results=999 -O $backdir/blog_all_xml_feeds.html
wget http://chowroc.blogspot.com/feeds/comments/default?max-results=999 -O $backdir/blog_all_comments.html

这个会生成 3 个 html 文件,包含所有文章。

不过,也是为了一种备份的需要,我决定现在开始使用百度空间,那么以后在 blogspot 上粘贴的文章,在这里也帖一份。不太敢使用博客搬家的功能,因为需要向服务器提交另一个 blog 地址的账户信息,那意味着这些程序将拥有完全的权限,甚至删掉以前的东西。事实上,好像有些博客搬家就是这么干的。我需要的实际上是博客镜像而不是搬家,所以我宁可麻烦一点也不去冒这个风险。

以前的文章不会再贴在这里,要访问以前的内容,到:
http://chowroc.blogspot.com/

因为朝廷构建的 GFW,一般情况下是没办法访问的,不过所幸我还知道一些方法可以比较快的访问。不过这里就不广泛传播了,一则意义不大,再则知道的人多了,气味太浓又要被鹰犬们嗅到了 :P

星期四, 十月 11, 2007

Some Distributed Design Principles

Given what we have covered so far, we can define some fundamental design principles which every distributed system designer and software engineer should know. Some of these may seem obvious, but it will be helpful as we proceed to have a good starting list.

* As Ken Arnold says: "You have to design distributed systems with the expectation of failure." Avoid making assumptions that any component in the system is in a particular state. A classic error scenario is for a process to send data to a process running on a second machine. The process on the first machine receives some data back and processes it, and then sends the results back to the second machine assuming it is ready to receive. Any number of things could have failed in the interim and the sending process must anticipate these possible failures.

* Explicitly define failure scenarios and identify how likely each one might occur. Make sure your code is thoroughly covered for the most likely ones.

* Both clients and servers must be able to deal with unresponsive senders/receivers.

* Think carefully about how much data you send over the network. Minimize traffic as much as possible.

* Latency is the time between initiating a request for data and the beginning of the actual data transfer. Minimizing latency sometimes comes down to a question of whether you should make many little calls/data transfers or one big call/data transfer. The way to make this decision is to experiment. Do small tests to identify the best compromise.

* Don't assume that data sent across a network (or even sent from disk to disk in a rack) is the same data when it arrives. If you must be sure, do checksums or validity checks on data to verify that the data has not changed.

* Caches and replication strategies are methods for dealing with state across components. We try to minimize stateful components in distributed systems, but it's challenging. State is something held in one place on behalf of a process that is in another place, something that cannot be reconstructed by any other component. If it can be reconstructed it's a cache. Caches can be helpful in mitigating the risks of maintaining state across components. But cached data can become stale, so there may need to be a policy for validating a cached data item before using it.

If a process stores information that can't be reconstructed, then problems arise. One possible question is, "Are you now a single point of failure?" I have to talk to you now - I can't talk to anyone else. So what happens if you go down? To deal with this issue, you could be replicated. Replication strategies are also useful in mitigating the risks of maintaining state. But there are challenges here too: What if I talk to one replicant and modify some data, then I talk to another? Is that modification guaranteed to have already arrived at the other? What happens if the network gets partitioned and the replicants can't talk to each other? Can anybody proceed?

There are a set of tradeoffs in deciding how and where to maintain state, and when to use caches and replication. It's more difficult to run small tests in these scenarios because of the overhead in setting up the different mechanisms.

* Be sensitive to speed and performance. Take time to determine which parts of your system can have a significant impact on performance: Where are the bottlenecks and why? Devise small tests you can do to evaluate alternatives. Profile and measure to learn more. Talk to your colleagues about these alternatives and your results, and decide on the best solution.

* Acks are expensive and tend to be avoided in distributed systems wherever possible.

* Retransmission is costly. It's important to experiment so you can tune the delay that prompts a retransmission to be optimal.

From:

http://code.google.com/edu/parallel/dsd-tutorial.html

星期二, 十月 09, 2007

数据库连接与 /etc/hosts 设置

上次数据库出问题后更换了主机,但之后时常出现"Can not connect to database"的错误,考虑了若干中可能,但最有效的还是先查看系统日志 /var/log/message:
Oct  9 10:49:31 shopex mysqld: gethostby*.getanswer: asked for "210.0.168.192.in-addr.arpa IN PTR", got type "A"
...
Oct 9 10:49:32 shopex mysqld: gethostby*.getanswer: asked for "210.0.168.192.in-addr.arpa IN PTR", got type "A"
这说明是地址解析的问题,mysqld 中设置了反向 DNS 解析。一般只需要在 /etc/hosts 中增加相应的记录即可:
192.168.0.210   www