2008年March月 发布的文章

Admire 龙芯电脑

当机器是 512M 内存的时候,只能用 64bit kernel,否则只能用 256M 内存(这个还能理解)
当机器是 256M 内存的时候,只能用 32bit kernel,否则就会随机死机,是彻底死掉,不是 panic。

Reiserfs 看来真是要倒台了。

今天某站的 reiserfs 又不成了,列不出来目录,重启以后无法 mount。我在被窝里面被电话吵醒,然后起来 fsck.reiserfs。根据上次某人的经验,就用 fsck.reiserfs 做一次 –fix-fixable 就可以了。不过这个过程超过想象的漫长。

中间我准备上 reiserfs 的官方网站 www.namesys.com 上面看看,却发现域名已经无法解析,whois 结果如下

Registrant:
Namesys
Hans Reiser
6979 Exeter Dr
Oakland, CA 94611
US
Email: reiser@namesys.com

Registrar Name….: REGISTER.COM, INC.
Registrar Whois…: whois.register.com
Registrar Homepage: www.register.com

Domain Name: namesys.com

Created on…………..: Fri, Mar 24, 2000
Expires on…………..: Tue, Mar 24, 2009
Record last updated on..: Tue, Mar 06, 2007

看起来好像还没过期,不过也很有可能是注册商给多续了一年保护,以敲诈更多银两。 反正不管怎么说, reiserfs 从 Hans Reiser 进局子以后,怎么看怎么都像不太成了的样子,现在连主页都能倒掉。可怜我们这些套牢在 reiserfs 上面的用户了。

无责任闲谈 – LD_PRELOAD 和模块内调用

无责任闲谈 – LD_PRELOAD 和模块内调用

这个问题的提出是因为某天伟大的布总问了我一个问题。

如果某个 .so 里面有两个函数, 一个 foo ,一个 bar, bar 调用了 foo ,那么 bar 调用 foo 的地方应该是直接生成对 foo 的调用呢?还是通过跳转表来实现呢?

我们讨论了一下,都觉得如果没有加 -fPIC 的话,这个怎么也不应该通过跳转表来实现。但是布总却发现,如果之前弄一个 inject.so 里面包含一个叫做 foo 的函数,然后通过 LD_PRELOAD 来预先加载,不管编译时候有没有加 -fPIC,调用 bar 的时候,调用的都是 inject.so 里面的 foo 函数。这就有点奇怪了,ld-linux.so 怎么会去修改原来 .so 中 foo 对 bar 的调用呢?

还是反汇编一下看看吧:

不带 -fPIC

00000498 :
498: 55 push %ebp
499: 89 e5 mov %esp,%ebp
49b: 5d pop %ebp
49c: c3 ret

0000049d :
49d: 55 push %ebp
49e: 89 e5 mov %esp,%ebp
4a0: e8 fc ff ff ff call 4a1
4a5: 5d pop %ebp

带 -fPIC
000004a8 :
4a8: 55 push %ebp
4a9: 89 e5 mov %esp,%ebp
4ab: 5d pop %ebp
4ac: c3 ret

000004ad :
4ad: 55 push %ebp
4ae: 89 e5 mov %esp,%ebp
4b0: 53 push %ebx
4b1: 83 ec 04 sub $0x4,%esp
4b4: e8 eb ff ff ff call 4a4 <__i686.get_pc_thunk.bx>
4b9: 81 c3 4b 11 00 00 add $0x114b,%ebx
4bf: e8 fc fe ff ff call 3c0 // PLT thunk
4c4: 83 c4 04 add $0x4,%esp
4c7: 5b pop %ebx
4c8: 5d pop %ebp

其中相关的 PLT 内容如下:

000003c0 :
3c0: ff a3 10 00 00 00 jmp *0x10(%ebx) // 指向跳转表项的指针
3c6: 68 08 00 00 00 push $0x8
3cb: e9 d0 ff ff ff jmp 3a0 <_init+0x18>

-fPIC 的情况很好理解,跳转都是通过 PLT 进行的,直接去修改跳转表中的数据即可。那么没有 -fPIC 的情况,loader 是怎么能够修改调用让他指向另外 .so 里面的函数呢?

从汇编里面可以看出来,没有 -fPIC 的情况下面,对 foo 的调用指向的也不是 foo ,而是一个没有意义的地址,观察 .so 的 reloc 信息可知,有一条 reloc 指向 bar 对 foo 调用的地方,内容是 “foo”

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
000015fc R_386_RELATIVE *ABS*
00001600 R_386_RELATIVE *ABS*
000004a1 R_386_PC32 foo

因此在 LD_PRELOAD 的情况下面, loader 会查找 foo 的地址并填入 bar 对 foo 调用的地方。

这种按照名字 reloc 的方式在 Win32 下面似乎没有见到过。Win32 下面 reloc 只是用于修正 DLL 加载以后导致数据指针位置不对的问题,操作方法是简单的把位置上面的值减掉原基址加上新基址,相当于 R_386_RELATIVE 这种类型的 reloc。但是仔细想想,这种 reloc 其实并没有任何必要,RELATIVE 方式已经足够解决问题了,怀疑这个 PC32 的方式,就是单独为了支持 LD_PRELOAD 这一历史遗留产物而设计的。(我比较懒得查阅资料,谁查到了麻烦讲一下)

在 64 位下面,这个把戏不能用了,因为 64bit 下面函数调用一般还只是通过 E8 指令来调用 32 位范围内的地址,而 foo 函数的位置可能离 bar 调用它的地方超过了 2G,因此 ld 遇到这样的 reloc 会报告失败,要求你必须用 -fPIC 来编译。

有人可能会问, -fPIC 了不是会影响性能?这个确实没办法了,在很久以前大家没有意识到需要 module local symbol ,觉得除了 static 的所有函数都全局可见就好了,结果这个习惯就一直继承下来了,虽然现在 ELF 格式中确实支持 module local 的 symbol。但是因为历史上就是这么下来的,为了维持兼容性,只好继续保持这一行为。很多函数并没有跨模块调用的需要,只是因为有跨文件调用就不得不变成非 static 的,在上面说的这种情况下就会受到影响。

一个很土鳖的解决方法就是把所有函数都堆进一个 .c ,然后除了真正需要导出的函数,都改成 static 的。也许 gcc 现在支持什么扩展,可以将符号声明成 module local 的,不过我也是懒得查资料了,有人知道的话,请不吝赐教。 //bow

RPM vs DEB的争论

RPM vs DEB的争论

这个论题也算是一个老坑了,我总是看到很多人说 debian 系统如何如何好使,apt 多么多么强。而 RPM 系统里面包依赖关系多么乱啥的。因此得出 DEB 这个包管理系统要比 RPM 这个包管理系统要强的结论。不过在我看来,这些问题要么不是问题,要么跟包管理系统一点关系都没有。

先说 DEB 派的几个论据

1: apt 系统用起来很方便
对, apt 系统用起来确实很方便,但这是在 rh9 时代的事情。现代基于 RPM 的系统,基本都有相对还不错的管理器,比如 fc 用的 yum, suse 用的 yast 等,虽然这些玩意可能也有点小毛病,比如 yum 相对 apt 来说要慢一些,但是总体来说,并不是致命的缺陷。只要有容量比较大的源,一样可以很爽的用。目前看来, FC 正在往 deb 的大源方向发展,FC 的官方源里面东西也相当的多了。
2: debian 系统比 redhat 系列的好使
这个说老实话,没有特别觉出来,当然这个跟使用习惯有关。倒是我觉得 debian 太过顽固,居然能跟 mozilla 打起来,搞的 firefox 都得改名成 iceweasel,导致一些网页上面的认 UA 的 javascript 不好使。相比之下,ubuntu 比起来 debian 这样的革命者就好不少,至少不会因为意识形态的问题跟人打架。
3: RPM 包的依赖关系比较混乱
这个我其实觉得跟 RPM 本身没有什么关系,倒是跟做 RPM 的人的习惯有关系。不知道是什么历史原因导致的,RPM 发行版的打包者通常很喜欢把一个包拆得很碎,连一个 snmp 都要拆成三个包,而 deb 系统在这方面就没有这么激进,通常就是一个软件拆成 base 和 dev ,只有比较大的包才会考虑分一下。这样就造成 RPM 发行版的依赖关系要更加复杂,这些依赖关系都是人手写的,维护代价上升很可能就造成一些混乱。这个确实是 RPM 发行版存在的问题,不过只要你自己会 build rpm, 那么基本依赖关系还是可以解决的。

下面说我自己感觉 RPM 系统优于 DEB 系统的地方

1: 打包容易。RPM 系统只需要写一个 .spec 文件就可以打包了,而 DEB 的我看了半天说明也还是没有找到门道。还要建一堆目录啥的,这对专门的打包者可能不是个问题,对我就是想临时 build 一下的人来说,就有点浪费了。
2: 有 rpmfind.net 这个好网站。deb 给我的感觉似乎是想把世上所有的软件都给吸纳进源,确实 debian 的源容量相当之大,很多奇怪的软件也都在 deb 源里面,但是经常还是有些东西找不到的,这时候 rpmfind 网站就很好用,我经常的用法是在上面找到 SRPM 然后下来自己改改 .spec build 一个适合自己系统的出来。 而 deb 似乎如果源里面没有而你不掌握打包技能,那么基本你就只能当 ./configure 一族了。
3: 配置文件拆分不那么激进。跟拆软件包的策略正好相反,保守的 deb 在拆配置文件上面下的力气,要比 rpm 系列大很多,拿 httpd 来说, Redhat 系列直接就拆成一个主的和一堆散的放在 conf.d 下面的就好了。而 debian 则是拆成了很多小的放在 mods-available 目录里面,需要什么,就 ln-s 到 mods-enabled 里面。这样看起来挺好,但是配置文件这东西哪里是那么好拆的,有些逻辑不好处理,于是他又搞出来一个 xxx.load 和 xxx.conf 的东西。最后搞到我都不知道他这个配置文件到底包含了什么。配置一个我本来会配置的东西,还要研究半天他怎么拆的这个配置,搞的很麻烦。当然这个我承认是个人喜好问题。但是我认为作网站运维的时候,应该使配置文件简单化,只完成够用的功能,而不应该为了扩展方便弄成这么一堆花儿,回头出问题了排查都麻烦。
4: 商业软件对 RPM 系统的支持更好一些
很多商业软件只有 rpm 版本的,或者安装脚本就是按照 RPM 风格的系统配置文件做的。deb 系统下面虽然有 alien 这个工具可以转 rpm ,但是并不能转里面软件对系统路径的依赖,很多软件都是到固定的地方去读取系统配置或者调用系统程序的。

基于以上原因,我在我维护的所有 Linux 系统上面都是采用基于 RPM 的系统。而且我见到的所有用 Linux 的大型网络公司,基本都是在用基于 RPM 的发行版跑服务。我想很多情况下原因也就是在于,在运维人员看来,rpm 和 deb 都差不多,都可以用得很爽,而 RPM 发行版在商业软件支持上有小小优势,就用 RPM 发行版了。

最近评论

时光机

其他