Linux系统及程序的启动速度
这篇文章写了有段时间了,不过没有在这里贴过。现在拿出来和大家分享一下。如果有理解错误的地方,还望指正。
Linux(包括其上运行的应用程序)的启动速度相比Windows要慢,这是个不争的事实。不过未来这种状况必将会发生变化。lwn.net上有两篇文章与此话题有关。
首先,要明确的一点是,导致这种状况的根本原因与内核关系不大。两个主要原因,一个是用户层程序启动时往往会做很多无谓的动作,可以从strace的结果里体会到;一个是ELF(现代*nix系统所采用的可执行文件和共享库文件格式)格式的二进制程序在符号解析时要耗费不少时间。lwn.net上的这两篇文章就分别针对这两个问题。
第一篇是http://lwn.net/Articles/192214/
第二篇是http://lwn.net/Articles/192624/
第一篇是Dave Jones在渥太华Linux研讨会(Ottawa Linux Symposium)的一个演讲,这个pdf的第441页 http://www.linuxsymposium.org/2006/linuxsymposium_procv1.pdf 就有这篇文章,题目是"Why user space sucks"。Dave观察了他的Fedora系统的启动过程,结果发现整个过程调用了stat() 79000次,打开过27000个文件和运行了1382个程序。但实际上不一定需要这么多动作。他还具体分析了很多程序的表现,比如gamin(fam ——file alteration monitor——的后继者),Xorg,CUPS,HAL等等。虽然Dave没有提出具体的解决方案,但是自此以后这个问题必定会引起人们的重视。
第二篇题目是Optimizing linker load times。文章开头提到目前很多发行版都在想尽办法提高系统启动速度,目前已有一些途径,比如预读取(readahead),启动重排序(boot reordering),还有并行执行init脚本。还有一个途径就是文章的主题,提高连接器动态连接的速度,主要是符号解析的速度。符号解析的目的,是把在程序里未定义的函数名解析为运行时该函数的内存地址。解析的过程就是查hash表,按惯常做法表里hash值相同的符号(就是未定义的函数名)组成链表。文章提到目前已经有的一个策略,就是prelink,这种方法的本质是修改二进制程序文件,在程序运行前就确定下来每个共享库文件在程序执行时的地址空间里的位置,当然包括每个函数的地址。但是prelink有一定的安全隐患。同一个共享库在prelink过的所有程序的地址空间里的位置都是一样的。虽然prelink也可以做地址空间随机化(address space randomization),但是这只能保证你的系统和其他人的系统里的应用程序的地址空间分布不一样,你的系统里所有的程序的地址空间虽然随机化了,但所有程序的随机化结果都是一样的。这样我们就能通过一些普通的程序推知将以root权限运行的程序的地址空间分布。地址空间分布的信息的暴露会使 return-to-libc攻击变得容易。文章的主体介绍了4种其他形式的动态连接优化形式。其中一种是旧的,已经获的广泛使用的-Wl,-O1选项。其他三种是新的,由Michael Meeks提出的新的机制。分别是-Bdirect, -zdynsort和预先计算hash值。第一种的解决方式是扩大hash表的大小,以减少链表的长度。-Bdirect的策略是在可执行文件里记录下每个符号来自于哪个共享库。以前没有-Bdirect的时候需要搜索地址空间里所有的共享库。-zdynsort的策略是把ELF文件里的.dynsym 和.dynstr段排序,以减少CPU cache miss的几率。预先计算hash值的策略很易懂,就是在生成可执行文件的时候就把hash值算出来,保存在文件里。这样执行时的动态连接过程中就不要再算了。这3个新策略里后两个很有可能马上就可以用上。目前,gentoo用户可以利用后面这个连接里提供的overlay来尝试这些新特性。 http://forums.gentoo.org/viewtopic-t-435659.html
Linux(包括其上运行的应用程序)的启动速度相比Windows要慢,这是个不争的事实。不过未来这种状况必将会发生变化。lwn.net上有两篇文章与此话题有关。
首先,要明确的一点是,导致这种状况的根本原因与内核关系不大。两个主要原因,一个是用户层程序启动时往往会做很多无谓的动作,可以从strace的结果里体会到;一个是ELF(现代*nix系统所采用的可执行文件和共享库文件格式)格式的二进制程序在符号解析时要耗费不少时间。lwn.net上的这两篇文章就分别针对这两个问题。
第一篇是http://lwn.net/Articles/192214/
第二篇是http://lwn.net/Articles/192624/
第一篇是Dave Jones在渥太华Linux研讨会(Ottawa Linux Symposium)的一个演讲,这个pdf的第441页 http://www.linuxsymposium.org/2006/linuxsymposium_procv1.pdf 就有这篇文章,题目是"Why user space sucks"。Dave观察了他的Fedora系统的启动过程,结果发现整个过程调用了stat() 79000次,打开过27000个文件和运行了1382个程序。但实际上不一定需要这么多动作。他还具体分析了很多程序的表现,比如gamin(fam ——file alteration monitor——的后继者),Xorg,CUPS,HAL等等。虽然Dave没有提出具体的解决方案,但是自此以后这个问题必定会引起人们的重视。
第二篇题目是Optimizing linker load times。文章开头提到目前很多发行版都在想尽办法提高系统启动速度,目前已有一些途径,比如预读取(readahead),启动重排序(boot reordering),还有并行执行init脚本。还有一个途径就是文章的主题,提高连接器动态连接的速度,主要是符号解析的速度。符号解析的目的,是把在程序里未定义的函数名解析为运行时该函数的内存地址。解析的过程就是查hash表,按惯常做法表里hash值相同的符号(就是未定义的函数名)组成链表。文章提到目前已经有的一个策略,就是prelink,这种方法的本质是修改二进制程序文件,在程序运行前就确定下来每个共享库文件在程序执行时的地址空间里的位置,当然包括每个函数的地址。但是prelink有一定的安全隐患。同一个共享库在prelink过的所有程序的地址空间里的位置都是一样的。虽然prelink也可以做地址空间随机化(address space randomization),但是这只能保证你的系统和其他人的系统里的应用程序的地址空间分布不一样,你的系统里所有的程序的地址空间虽然随机化了,但所有程序的随机化结果都是一样的。这样我们就能通过一些普通的程序推知将以root权限运行的程序的地址空间分布。地址空间分布的信息的暴露会使 return-to-libc攻击变得容易。文章的主体介绍了4种其他形式的动态连接优化形式。其中一种是旧的,已经获的广泛使用的-Wl,-O1选项。其他三种是新的,由Michael Meeks提出的新的机制。分别是-Bdirect, -zdynsort和预先计算hash值。第一种的解决方式是扩大hash表的大小,以减少链表的长度。-Bdirect的策略是在可执行文件里记录下每个符号来自于哪个共享库。以前没有-Bdirect的时候需要搜索地址空间里所有的共享库。-zdynsort的策略是把ELF文件里的.dynsym 和.dynstr段排序,以减少CPU cache miss的几率。预先计算hash值的策略很易懂,就是在生成可执行文件的时候就把hash值算出来,保存在文件里。这样执行时的动态连接过程中就不要再算了。这3个新策略里后两个很有可能马上就可以用上。目前,gentoo用户可以利用后面这个连接里提供的overlay来尝试这些新特性。 http://forums.gentoo.org/viewtopic-t-435659.html
Comments