在's profile不在PhotosBlogLists Tools Help

Blog


    04/11/2006

    NetBSD Bookmarks

    Download Systems


    News


    Main Websites


    Packages


    Blogs


    Forums


    Other Websites


    User's Docs


    PDF Docs


    NetBSD LiveCD, Floppy, Flash and Others


    NetBSD Books


    NetBSD Culture


    NetBSD: Articles, Advocacy, Reviews


    NetBSD - Technical Articles in Polish (warpman's selection)


    NetBSD - Technical Articles in English (warpman's selection)


    Firms Offering NetBSD Systems

    • Polish
    • Foreign
    • iXsystems - the firm acquired PC-BSD
      iXsystems is a leading provider of high-performance computing clusters, blade servers, rackmount servers, and storage solutions to the global marketplace. iXsystems supplies FreeBSD, NetBSD, OpenBSD and Linux servers to a wide cross-section of industries."

    Varia


    29/05/2006

    OnLamp.com的Clean up ports

     
    FreeBSD ports即便得到所有赞美,也存有局限性。其中之一实际上来自它的强项-upgrade系统。两者以一种郁闷的方式互相影响。
    port安装过程记录一个port所include的所有文件和所有依赖。比如,许多ports需要PNG图形库。port记录它使用的PNG版本,如1.0.11。当你安装了一部分软件,这一般和可以在ports树中找到是相同的版本。FreeBSD upgrade进程可以升级被port tree支持的软件版本。比如,PNG最近升级到1.0.12版本,库版本号没有增加,但是新库有一些小bug修复。我想升级。但是如果我那样做了,编码了确切PNG版本号的30多个ports将得到错误记录。
    ?
    我的笔记本有187个包(packages)。我一个星期升级一次。这很快就变得零乱,不久之后就难以忍受。幸好,Akinori MUSHA has stepped up to the plate(怎么翻译)并开发了一套可以整理这种零乱状态的工具:portupgrade(1)和他的朋友们(小熊温尼和他的朋友们)。
    portupgrade中包含的工具实现了port系统的新特性。首先是pkgdb
    和portdb,用来对/var/db/pkg索引和ports树创建数据库。这样能加快搜索并操作之。这些工具也可以重写/var/db/pkg里不同的文件来维护一致性。最后是各种pkg_*命令的wrappers,当添加或删除软件时可以用来重写数据库和纯文本记录。所有这些可以通过安装/usr/ports/sysutilis/portupgrade来得到。
    在开始使用portupgrade之前,备份 /var/db/pkg!我从没在这个程序上遇到过麻烦,但是任何可以直接改写系统记录的工具具有无限可能使你的生活一团糟。如果你完全破坏了package records,你需要还原它:
    #tar -czvf dbpkg.tgz /var/db/pkg
    运行pkgdb -F 来初始化安装,并进行基本的清理工作。如果你有许多ports,并已升级过几次port树。第一次时应留出一定时间。一旦有了一致性的数据库,维护将是又快又easy的;只是第一次会比较耗时。
     
      #pkgdb -F
      Checking the origin of Hermes-1.3.2
      Checking the origin of Mesa-3.4.1
      Checking the origin of Mesa-3.4.2_1
      Checking the origin of ORBit-0.5.10_5
      Checking the origin of XFree86-aoutlibs-3.3.6.9

    当遇到依赖改变的包时,pkgdb会询问是否要修改。

      Stale dependency: esound-0.2.22 -> libaudiofile-0.2.2:
      libaudiofile-0.2.1_1 ? ([y]es/[n]o/[a]ll) [yes]

    esound已经记录libaudiofile版本0.2.2作为依赖,但是安装的是版本0.2.1_1。我也许过去做过pkg_add -f,以强制安装尽管有微小版本差异。问我是否想要更新我在/var/db/pkg/esound-0.2.22/+CONTENTS的入口来指向实际安装的libaudiofile的版本。如果默认,他将修改入口记录,如果输入“a”,不仅修改libaudiofile对于esound的入口,而且还修改所有用到libaudiofile的其他包。这对于具有很多依赖的包比如PNG,显得格外方便。
    在之前的例子里,pkgdb可以对依赖性进行智能推测。另一方面,它不能推测所有事情。比如,我从xfree86.org的CVS主机上编译XFree86.imake是free86的一部分,所以在/var/db/pkg中没有相应记录。好;port编译过程check已安装的,并且没有在/var/db/pkg记录的包。当一个port安装后,记录写进/var/db/pkg中,然而前提是如go建立程序port在那里。(这是一个可论证的port的缺陷,但是还没有简易的修复办法)
    这意味着当一个ports列出imake作为依赖时,pkgdb将卡壳

      Stale dependency: Xaw3d-1.5 -> imake-4.1.0:
      New dependency? (? to help): ?

    这里我有一个选项。我可以指派一个新的依赖,或我可以告知package已经不再有什么依赖了。当Xaw3d需要imake,我知道在这个特别的系统里,它不会发现imake已经安装了。我想要删除依赖性。如果敲”?”来查看帮助会发现:

      [Enter] to skip, [Ctrl]+[D] to delete, [.][Enter] to abort, [Tab] to complete
      New dependency? (? to help):?

       

      Control-D it is.
      Delete this? ([y]es/[n]o/[a]ll) [yes]
      Deleted.

    如果我选择“YES”,将清除该依赖。如果我选择“all”将清除所有该依赖无论何时出现。许多ports使用imake;我都想清除。稍后你将看到:

      Stale dependency: xneko-4.4 -> XFree86-libraries-4.1.0:
      Delete this? ([y]es/[n]o/[a]ll) [yes] Deleted.

    还有几次,在别处也出现了pkgdb不能推测依赖的情况

      Stale dependency: plugger-3.3_1 -> timidity++-esound-2.10.4:
      New dependency? (? to help): ?
      [Enter] to skip, [Ctrl]+[D] to delete, [.][Enter] to abort, [Tab] to complete
      New dependency? (? to help): timidity++-2.10.4

    用tab可以完成。我知道timidity++-esound用到某版本的timedity,但是我不想挖地三尺以求整个完整的版本号。我可以输入前两个字母然后按Tab键,就得到了剩下的包的名字。
    最后,在整个过程的最后,pkgdb将提请你关于副本:

      Duplicated origin: graphics/Mesa3 - Mesa-3.4.1 Mesa-3.4.2_1
      Remove any of them? [no]

    这里我有一个问题。从一个包安装软件而需要另外一个包的情况是很疏松平常的。也许你已安装的包有一点版本出入。如果你正通过package来安装,它会自动安装依赖,即使稍有出入的版本已经安装了。这有时会在我的系统中出现。我的本本有两个不同版本的Mesa,但是一个已经覆盖了另一个。这明显的不能算最糟糕的,因为我的系统还在工作。(最后我完全删除了mesa并安装了适合的版本)。尽管如此我依然需要清理packge数据库。我手动卸载了老版本。
    现在你有了package的数据库信息,/var/db/pkg/pkgdb.db。也有了一个port数据库,/usr/ports/index.db和/usr/ports/index.dbo.各种portuprade工具使用这些数据库来完成魔术般的操作.无论何时升级ports树,都要升级ports数据库和/usr/ports/INDEX.可以通过portsdb -Uu来轻易完成.
    现在我们已经装好了工具,来看看我们可以干些什么.想看哪些是过时的,使用portversion(1).这个命令和pkg_info的工作方式相同,但是速度更快.

      # portversion
      Hermes =
      Mesa =
      ORBit <
      XFree86-aoutlibs <
      Xaw3d =
      aalib =

    <代表安装的版本比port树中的旧.可以通过运行portversion | grep '<'来查看哪些port过时了.我经常使用它来查看自己的系统.比如,我的Apache过时了.但是在我的本本上我已经不使用apache了,所以我可以删除它.标准的pkg_delete命令仍然适用,但是我用了,我的package数据库将同我的系统失去同步.使用pkg_deinstall来代替.

      # pkg_deinstall apache
      —< Deinstalling 'apache-1.3.20'
      [Updating the pkgdb in /var/db/pkg ... - 182 packages found (-1 +0) (...) done]
      #

    好处是我再也不用给出完整版本号!(对于FreeBSD的pkg_*,这困扰我良久,但是无能为力).portupgrade工具包含全局函数,可以进行模式匹配。
    我打算升级特定的ports,而且,我要升级的同时保持数据库同步,跟新入口记录到/var/db/pkg。比如portversion高速我gd过时了。升级工作很简单:

      # portupgrade gd
      ===> Cleaning for gettext-0.10.35
      ===> Cleaning for gmake-3.79.1
      ===> Cleaning for libtool-1.3.4_2
      ===> Cleaning for jpeg-6b
      ===> Cleaning for png-1.2.0
      ===> Cleaning for freetype2-2.0.5
      ===> Cleaning for gd-1.8.4_4
      If you want to compile in X support use
      ‘make -DWITH_X11′ instead
      ===> Extracting for gd-1.8.4_4
      ….

    将看到熟悉的make输出界面。如果继续观察,会发现转而去卸载已安装的包,更新数据库,然后继续。

      … —> Deinstalling ‘gd-1.8.4_3′
      pkg_delete: package ‘gd-1.8.4_3′ is required by these other packages and may not be deinstalled (but I’ll delete it anyway):
      scr2png-1.1
      [Updating the pkgdb in /var/db/pkg … - 180 packages found (-1 +0) (…) done]
      ===> Installing for gd-1.8.4_4

    一旦安装完成,会再一次更新package数据库。所有的都是同步的。
    现在让我们看一个难点的。我的docproj port过时了。docporj是不包含任何东西的,但是同每个编辑FreeBSD Document Project的工具有依赖。如果经常建立文档树,这个工具需要更新。还有,你不想递归重编译所有,只想做小的更新;docproj有着庞大的依赖。
    如果打算节约时间,可以用portupgrade -P来告诉portupgrade使用package安装。我不缺时间所以我用port。我们需要告知portupgrade来递归更新,将更新被docproj用到的包。可以使用-R标志,试试:

      # portupgrade -R docproj
      This port will try to ensure that the tools used by the FreeBSD
      Documentation Project are installed on your system so you can convert documentation from SGML to other formats.

    组件其中之一是JadeTex,它依赖于TeTex。TeTex的源代码超过30M,可能需要很长的下载时间。
    如果不打算从文档产生PostScript和PDF格式,就不需要JadeTex,你应当将JADETEX变量设为“NO”。如果希望输出PostScript和PDF,则设为“YES”。
    比如:

      make JADETEX=no
      make JADETEX=yes?

       

      *** Error code 1

      Stop.
      ** Command failed: make clean build
      ** Fix the problem and try again.
      ** The following packages were not installed or upgraded (-:skipped /
      !:failed)
      ! textproc/docproj (docproj-1.4) (unknown build error)
      #

    我考。这个port需要定制。你可以修改/usr/ports/textproc/docproj/Makefile添加 JADETEX=no。现在它将完全透明得运行。
    完成后,可以用portversion来查看发生了什么。你可能会发现其他的port依赖也已改写。比如,如果port A依赖于Port B,但是port C也依赖于Port B,运行portupgrade A将不会修改Port C的依赖。随时准备用pkgdb -F来发现和修复类似问题。
    如果你打算让系统自己来处理这些事情,使用portupgrade -rR;这将升级此依赖的和依赖于此的ports。如果想要自己的系统有最新的软件,这里提供了一种方式。portupgrade包含了使FB软件管理简单化的多种工具,但这足以使你起步。
    你可能会发现不得不从一个相同的distfile重编译一个port很多次。如果port改变了,portversion将发现并标记为过时。你想保留安装了的port的distfile,然后删除旧的distfile。portsclean -D将完成这些工作。相同的,你可能打算删除在package数据库中不被引用的共享库。我不使用上面的参数。别忘了我安装XFree86以抓包的方式而不是port。这个命令(portclean -D)将会删除我的X安装,我考!但是portsclean -L可以办到,并且可以帮助处理一些问题,比如我的不完全安装的Mesa的垃圾文件。
    最后,我必须说这个工具似乎相当鲁棒。文章的结尾,我运行这个程序来证明我的说法。我注意到了一个旧的包,简单的升级了它:

      # portupgrade -R portupgrade

    是的,没错它可以,我说过的它真是好样的。

    original:Michael W. Lucas
    tans. wonglaye

    26/05/2006

    Linux日志文件系统及性能分析(组图)

      日志文件系统可以在系统发生断电或者其它系统故障时保证整体数据的完整性,Linux是目前支持日志文件系统最多的操作系统之一,本文重点研究了Linux常用的日志文件系统:EXT3、ReiserFS、XFS和JFS日志技术,并采用标准的测试工具PostMark和 Bonnie++对它们进行了测试,给出了详细的性能分析,对Linux服务器应用具有重要的参考价值。
      
      一、概述
      
      所谓日志文件系统是在传统文件系统的基础上,加入文件系统更改的日志记录,它的设计思想是:跟踪记录文件系统的变化,并将变化内容记录入日志。日志文件系统在磁盘分区中保存有日志记录,写操作首先是对记录文件进行操作,若整个写操作由于某种原因(如系统掉电)而中断,系统重启时,会根据日志记录来恢复中断前的写操作。在日志文件系统中,所有的文件系统的变化都被记录到日志,每隔一定时间,文件系统会将更新后的元数据及文件内容写入磁盘。在对元数据做任何改变以前,文件系统驱动程序会向日志中写入一个条目,这个条目描述了它将要做些什么,然后它修改元数据。目前Linux的日志文件系统主要有:在Ext2基础上开发的Ext3,根据面向对象思想设计的ReiserFS,由SGI IRIX系统移植过来的XFS,由IBM AIX系统移植过来的JFS,其中EXT3完全兼容EXT2,其磁盘结构和EXT2完全一样,只是加入日志技术;而后三种文件系统广泛使用了B树以提高文件系统的效率。
      
      二、Ext3
      
      Ext3 文件系统是直接从Ext2文件系统发展而来,目前Ext3文件系统已经非常稳定可靠,它完全兼容Ext2文件系统,用户可以平滑地过渡到一个日志功能健全的文件系统。Ext3日志文件系统的思想就是对文件系统进行的任何高级修改都分两步进行。首先,把待写块的一个副本存放在日志中;其次,当发往日志的 I/O 数据传送完成时(即数据提交到日志),块就写入文件系统。当发往文件系统的I/O 数据传送终止时(即数据提交给文件系统),日志中的块副本就被丢弃。
      
      2.1 Ext3日志模式
      
      Ext3既可以只对元数据做日志,也可以同时对文件数据块做日志。具体来说,Ext3提供以下三种日志模式:
      
      日志(Journal )
      
      文件系统所有数据和元数据的改变都记入日志。这种模式减少了丢失每个文件所作修改的机会,但是它需要很多额外的磁盘访问。例如,当一个新文件被创建时,它的所有数据块都必须复制一份作为日志记录。这是最安全和最慢的Ext3日志模式。
      
      预定(Ordered )
      
      只有对文件系统元数据的改变才记入日志。然而,Ext3文件系统把元数据和相关的数据块进行分组,以便把元数据写入磁盘之前写入数据块。这样,就可以减少文件内数据损坏的机会;例如,确保增大文件的任何写访问都完全受日志的保护。这是缺省的Ext3 日志模式。
      
      写回(Writeback )
      
      只有对文件系统元数据的改变才记入日志;这是在其他日志文件系统发现的方法,也是最快的模式。
      
      2.2 日志块设备(JBD)
      
      Ext3 文件系统本身不处理日志,而是利用日志块设备(Journaling Block Device)或叫JBD 的通用内核层。Ext3文件系统调用JDB例程以确保在系统万一出现故障时它的后续操作不会损坏磁盘数据结构。Ext3 与JDB 之间的交互本质上基于三个基本单元:日志记录,原子操作和事务。
      
      日志记录本质上是文件系统将要发出的低级操作的描述。在某些日志文件系统中,日志记录只包括操作所修改的字节范围及字节在文件系统中的起始位置。然而,JDB 层使用的日志记录由低级操作所修改的整个缓冲区组成。这种方式可能浪费很多日志空间(例如,当低级操作仅仅改变位图的一个位时),但是,它还是相当快的,因为JBD 层直接对缓冲区和缓冲区首部进行操作。
      
      修改文件系统的任一系统调用都通常划分为操纵磁盘数据结构的一系列低级操作。如果这些低级操作还没有全部完成系统就意外宕机,就会损坏磁盘数据。为了防止数据损坏,Ext3文件系统必须确保每个系统调用以原子的方式进行处理。原子操作是对磁盘数据结构的一组低级操作,这组低级操作对应一个单独的高级操作。
      
      出于效率的原因,JBD 层对日志的处理采用分组的方法,即把属于几个原子操作处理的日志记录分组放在一个单独的事务中。此外,与一个处理相关的所有日志记录都必须包含在同一个事务中。一个事务的所有日志记录都存放在日志的连续块中。JBD层把每个事务作为整体来处理。例如,只有当包含在一个事务的日志记录中的所有数据提交给文件系统时才回收该事务所使用的块。
      
      三、ReiserFS
      
      ReiserFS 是一个非常优秀的文件系统,其开发者非常有魄力,整个文件系统完全是从头设计的。目前,ReiserFS可轻松管理上百G的文件系统,这在企业级应用中非常重要。ReiserFS 是根据面向对象的思想设计的,由语义层(semantic layer)和存储层(storage layer)组成。语义层主要是对对象命名空间的管理及对象接口的定义,以确定对象的功能。存储层主要是对磁盘空间的管理。语义层与存储层是通过键(key)联系的。语义层通过对对象名进行解析生成键,存储层通过键找到对象在磁盘上存储空间,键值是全局唯一的。
      
      3.1 语义层主要接口
      
      1) 文件接口 每个文件拥有一个接口ID,此ID标识一个方法集,此方法集包含访问ReiserFS 文件的所有接口。
      
      2) 属性接口 ReiserFS实现了一种新接口,把文件的每一种属性当做一个文件,属性的值就是此文件的内容,以实现对文件属性的目录式访问。
      
      3) hash接口 目录是文件名到文件的映射表,ReiserFS是通过B+树来实现这张映射表。由于文件名是变长的,而且有时文件名会很长,所以文件名不适合作为键值,故引入了Hash函数来产生键值。
      
      4) 安全接口 安全接口处理所有的安全性检查,通常是由文件接口触发的。下面以读文件为例:文件接口的read 方法在读入文件数据之前会调用安全接口的read chech 方法来来进行安全性检查,而后者又会调用属性文件的read方法把文件属性读入以便检查。
      
      5) 项(Item)接口 项接口主要是一些对项进行平衡处理的方法,包括:项的拆分,项的评估,项的覆写,项的追加,项的删除,插入及查找。
      
      6) 键分配(key Assignment)接口 当把一个键分配给一个项时,键分配接口就会被触发。每一种项都有一个与其对应的键分配方法。
      
      3.2 存储层
      
      ReiserFS是以B+树来存储数据的,其结构如图:
      
      图1:ReiserFS B+ 树
        
      在B+树中的各个结点中有一个称为项(Item)的数据结构。项是一个数据容器,一个项只属于一个结点,是结点管理空间的基本单位。如图所示,一个项包括以下内容:
      
      1) Item_body:项的数据域
      
      2) Item_key: 项的键值
      
      3) Item_offset:数据域的起点在结点中的偏移量
      
      4) Item_length: 数据域的长度
      
      5) Item_Plugin_id:项接口ID。
      
      图2: ReiserFS 项结构
        
      ReiserFS设计了多种不同的项以存储不同的数据,主要有以下几种:
      
      1) static_stat_data: 静态统计数据,包括文件的所有者,访问权限,创建时间,最近修改时间,链接数等
      
      2) cmpnd_dir_item: 包含各个目录项
      
      3) extend_pointers: 指向一个盘区(extend)
      
      4) node_pointers: 指向一个结点
      
      5) bodies: 包含的是文件的小部分数据
      
      3.3 ReiserFS日志
      
      与ext3 一样,ReiserFS也有三种日志模式,即journal,ordered,writeback。同时,ReiserFS引入了两种日志优化方法: copy-on-capture和steal-on-capture。copy-on-capture:当一个事务要修改的块在另一个未提交的事务中时,就把这个块复制一份,这样这两个事务就可以并发进行了。steal-on-capture:当一个块被多个事务修改时,只有最晚提交的那个事务才把这个块实际写入文件系统,其他事务都不写这个块。
      
      四、XFS
      
      XFS 是一种高性能的64 位文件系统,由SGI 公司为了替代原有的EFS 文件系统而开发的。XFS 通过保持cache 的一致性、定位数据和分布处理磁盘请求来提供对文件系统数据的低延迟、高带宽的访问。目前SGI已经将XFS文件系统从IRIX移植到Linux。
      
      4.1 分配组(allocation groups)
      
      当创建 XFS 文件系统时,底层块设备被分割成八个或更多个大小相等的线性区域(region),用户可以将它们想象成"块"(chunk)或者"线性范围(range)",在 XFS 中,每个区域称为一个"分配组"。分配组是唯一的,因为每个分配组管理自己的索引节点(inode)和空闲空间,实际上是将这些分配组转化为一种文件子系统,这些子系统透明地存在于 XFS 文件系统内。有了分配组,XFS 代码将允许多个线程和进程持续以并行方式运行,即使它们中的许多线程和进程正在同一文件系统上执行大规模 IO 操作。因此,将 XFS 与某些高端硬件相结合,将获得高性能而不会使文件系统成为瓶颈。分配组在内部使用高效的 B+树来跟踪主要数据,具有优越性能和极大的可扩展性。
      
      4.2 日志记录
      
      XFS 也是一种日志记录文件系统,它允许意外重新引导后的快速恢复。象 ReiserFS 一样,XFS 使用逻辑日志;它不象 ext3 那样将文字文件系统块记录到日志,而是使用一种高效的磁盘格式来记录元数据的变动。就 XFS 而言,逻辑日志记录是很适合的;在高端硬件上,日志经常是整个文件系统中争用最多的资源。通过使用节省空间的逻辑日志记录,可以将对日志的争用降至最小。另外,XFS 允许将日志存储在另一个块设备上,例如,另一个磁盘上的一个分区。这个特性很有用,它进一步改进了 XFS 文件系统的性能。
      
      4.3 延迟分配
      
      延迟分配是 XFS 独有的特性,它是查找空闲空间区域并用于存储新数据的过程。通过延迟分配,XFS 赢得了许多机会来优化写性能。到了要将数据写到磁盘的时候,XFS 能够以这种优化文件系统性能的方式,智能地分配空闲空间。尤其是,如果要将一批新数据添加到单一文件,XFS 可以在磁盘上分配一个单一、相邻区域来储存这些数据。如果 XFS 没有延迟它的分配决定,那么,它也许已经不知不觉地将数据写到了多个非相邻块中,从而显著地降低了写性能。但是,因为 XFS 延迟了它的分配决定,所以,它能够一下子写完数据,从而提高了写性能,并减少了整个文件系统的碎片。在性能上,延迟分配还有另一个优点。在要创建许多"短命的"临时文件的情况下,XFS 可能根本不需要将这些文件全部写到磁盘。因为从未给这些文件分配任何块,所以,也就不必释放任何块,甚至根本没有触及底层文件系统元数据。
      
      五、JFS
      
      JFS 由IBM 公司开发,最初出现在AIX 操作系统之上,它提供了基于日志的字节级、面向事务的高性能文件系统。它具有可伸缩性和健壮性,与非日志文件系统相比,它的优点是其快速重启能力:JFS 能够在几秒或几分钟内就把文件系统恢复到一致状态。JFS 是完全 64 位的文件系统。所有 JFS 文件系统结构化字段都是 64 位大小。这允许 JFS 同时支持大文件和大分区。
      
      为了支持 DCE DFS(分布式计算环境分布式文件系统),JFS 将磁盘空间分配池(称为聚集)的概念, 与可安装的文件系统子树(称为文件集)的概念分开。每个分区只有一个聚集;每个聚集可能有多个文件集。在第一个发行版中,JFS 仅支持每个聚集一个文件集;但是,所有元数据都已设计成适用于所有情况。
      
      如图3所示,聚集开始部分是32K的保留区,紧随其后的是聚集主超级块。超级块包含聚集的信息,例如:聚集的大小、分配组的大小、聚集块的尺寸等等。超级块位于固定位置,这使得 JFS 不依赖任何其它信息,就能够找到它们。在聚集中还有一个重要的结构是聚集索引结点表(Aggregate Inode Table)以及用于其映射的聚集索引结点分配映射表(Aggregate Inode Allocation Map)。AIT表中的inode 0 保留,inode 1 描述聚集本身,inode 2 描述聚集块映射表(block map), inode 3 描述安装时的内嵌日志,inode 4 描述在聚集格式化期间发现的坏块,保留inode 5 到 15 以备将来扩展。 从inode 16 开始,每个inode代表一个文件集。文件集中也有索引结点表以及用于其映射的索引结点分配映射表,文件集中的inode 描述文件集中的每一个文件。
      
      图3 JFS磁盘结构
        
      JFS 使用基于盘区的寻址结构,连同主动的块分配策略,产生紧凑、高效、可伸缩的结构,以将文件中的逻辑偏移量映射成磁盘上的物理地址。盘区是象一个单元那样分配给文件的相连块序列,可用一个由 <逻辑偏移量,长度,物理地址> 组成的三元组来描述。寻址结构是一棵 B+ 树,该树由盘区描述符(上面提到的三元组)填充,根在 inode 中,键为文件中的逻辑偏移量。
      
      JFS 按需为磁盘 inode 动态地分配空间,同时释放不再需要的空间。这一支持避开了在文件系统创建期间,为磁盘 inode 保留固定数量空间的传统方法,因此用户不再需要估计文件系统包含的文件和目录最大数目。另外,这一支持使磁盘 inode 与固定磁盘位置分离。
      
      JFS 提供两种不同的目录组织。第一种组织用于小目录,并且在目录的 inode 内存储目录内容。这就不再需要不同的目录块 I/O,同时也不再需要分配不同的存储器。最多可有 8 个项可直接存储在 inode 中,这些项不包括自己(.)和父(..)目录项,这两个项存储在 inode 中不同的区域内。第二种组织用于较大的目录,用按名字键控的 B+ 树表示每个目录。与传统无序的目录组织比较,它提供更快的目录查找、插入和删除能力。
      
      六、性能测试
      
      6.1 测试环境
        
      6.2测试工具
      
      所用的测试工具是Postmark和Bonnie++。Postmark主要用于测试文件系统在邮件系统或电子商务系统中性能,这类应用的特点是:需要频繁、大量地存取小文件。而Bonnie++主要测试大文件的IO性能。
      
      6.3 测试结果分析
      
      下面将详细分析用上述两种测试工具在各种测试参数配置下的结果。
      
      图4 PostMark 小文件
        
      图 4是PostMark测试小文件的结果,其参数是文件大小50B增至1K, 同一目录下的文件数从5k至20k,事务总数为25k。从图中我们可以看出:
      
      1. 不论是Ext3 还是ReiserFS,在三种日志模式中,写回(writeback)最快,预定(ordered)次之,日志(journal)最慢。
      
      2. 在各种文件系统中,ReiserFS 的写回和预定模式是最快的,且随着文件数的增加事务处理速度下降的也很慢。
      
      3. Ext3在文件数较少时,事务处理速度也比较快,但当文件数超过10k后,速度就比较慢了。
      
      4. XFS和JFS的速度较慢,但随着文件数的增加,速度下降的比较缓慢。
      
      图5 PostMark 大文件
        
      图5是PostMark测试大文件的结果,其参数是文件大小1k至16K,同一目录下的文件数从5k增至20k,事务总数为25k时的测试结果。从图中我们可以看出:
      
      1. 在处理大文件时,当文件数达到15k时,各种文件系统处理能力都较差。
      
      2. 当文件数在小于10k时,ReiserFS的写回、预定模式和EXT3的写回模式性能是比较好的。但这两种文件系统的全日志模式都比较差。
      
      3. XFS文件系统的性能居中,JFS文件系统的性能最差。
      
      图6:Bonnie++顺序写的速率
        
      图7:Bonnie++顺序写时CPU利用率
        
      图6是Bonnie++对文件大小分别为1G,2G,4G顺序写的性能比较,图7是其CPU的利用率比较。从上述两图中我们可以看出:
      
      1. 除了Ext3和ReiserFS的Journal模式的性能较差外,其他几种模式和XFS、JFS写磁盘的速率相当。
      
      2. 从CPU利用率来看,各种文件系统的CPU利用率都比较低,而且随着数据量的增大CPU的利用率降低。
      
      3. Journal模式的CPU利用率比其他两种模式要低。
      
      图8:Bonnie++ 顺序创建文件
        
      图9:Bonnie++ 随机创建文件
        
      图10:Bonnie++ 随机删除文件
        
      图11:Bonnie++ 随机删除文件时的CPU利用率
      
      图8至图11是Bonnie++对创建和删除文件的性能比较,文件数由50k增至400k。从中可以看出:
      
      1. 不管是创建文件,还是删除文件,Ext3和ReiserFS的三种日志模式之间的性能差别可以忽略不计。这主要是由于创建、删除文件都是对元数据的操作,而对元数据的操作三种模式之间本身就没有什么区别。
      
      2. 不管是创建文件,还是删除文件,Ext3的性能都比较差;ReiserFS的性能是最好的,特别是文件数少于100k时。这主要是由于Ext3是基于Ext2的,其目录项是线性组织的,而其他文件系统都是树形结构。
      
      3. 从CPU的利用率来看,除Ext3的利用率交给外,其他几种文件系统的利用率都很低。
      
      综上所述,我们可以得出以下结论:
      
      1. 在小型系统,如:邮件系统或小规模的电子商务系统应用时,ReiserFS和Ext3 的性能是比较好的。但由于Ext3的目录项是线型的,而ReiserFS的目录项是树型的,故当目录下文件较多时,ReiserFS的性能更优。
      
      2. 在对于上G的这种大文件做I/O时,各种文件系统间的性能差距很小,性能瓶颈往往在磁盘上。
      
      3. 虽然XFS和JFS在设计结构上都比较好,但它们主要是针对大中型系统的,在小型系统中由于硬件的原因性能发挥不明显。
      
      4. 全日志模式和预定、写回这两种模式相比,性能差距是比较大的;而预定和写回之间的性能差距不大。所以性能和安全兼顾时,文件系统的缺省安全模式,即预定模式是比较好的选择。

    17/03/2006

    谈谈GCC4.0几个值得关注的新特性

    本文侧重介绍了 GCC 4.0 内部结构相对于 3.4.x 版本的一些全新变化。 GCC(GNU Compiler Collection) 是 GNU(GNU's Not Unix) 计划提供的编译器家族,它能够支持 C, C++, Objective-C, Fortran, Java 和 Ada 等等程序设计语言前端,同时能够运行在 x86, x86-64, IA-64, PowerPC, SPARC 和 Alpha等等几乎目前所有的硬件平台上。鉴于这些特征,以及 GCC 编译代码的高效性,使得GCC 成为绝大多数自由软件开发编译的首选工具。虽然对于程序员们来说,编译器只是一个工具,除了开发和维护人员,很少有人关注编译器的发展,但是GCC的影响力是如此之大,它的性能提升甚至有望改善所有的自由软件的运行效率,同时它的内部结构的变化也体现出现代编译器发展的新特征,所以 2005年4月20日,GNU 组织发布的 GCC 4.0 引起了广泛的关注。那么这次 GCC 从 3.4.x 直接跃迁到 4.x 的主版本变化到底有什么值得关注的呢?

    我们可以从不同的角度看待 GCC 的这次变迁,对于普通程序员来说,关注的主要是GCC 的前端支持情况以及编译性能的变化。

    1. GCC 4.0 的前端支持

    GCC 的开发者和使用者当中,大多数人都是 C 或者 C++ 的用户,所以 GCC 对Fortran 语言支持不足也不令人奇怪。但是,这并不代表 Fortran 是无足轻重的,事实上,开发商业的 Fortran 编译器的公司要远远多于开发 C 和 C++ 编译器的公司。

    在科学计算和工程应用领域,程序员们仍然在频繁使用 Fortran 程序,同时,大量的经过长时间考验的函数库也为Fortran语言的数值计算提供了强有力的支持,所以,在一些"超级计算机"(supercomputer)上,Fortran仍然是绝大多数应用的首选语言。

    然而,在GCC 4.0发布之前,如果不想购买商用的Fortran编译器,那么程序员们的唯一选择就是GNU的g77编译器。但是g77编译器是一个相当陈旧的技术,很多Fortran语言的新特性都不能支持,比如流行的Fortran 95,它能够支持的模块化编程,并行处理和数组操作等等,g77编译器基本上都无法支持。

    这次GCC 4.0发布时推出了支持Fortran 95语言前端的编译器gfortran,它已经能够大大超越g77编译器,支持Fortran 95标准中的很多新特性,虽然gfortran还有一些缺陷,比如不能支持自动并行化(automatic parallelization),不能支持Fortran 2003中的面向对象特性等等,它已经给了Fortran程序员除了商业编译器和g77以外一个更好的选择。

    2. GCC 4.0的编译性能

    编译器的性能主要可以从三个方面来考查:

    1. 编译时间(compile time),指编译器编译一个源程序得到目标代码所需要的时间。

    2. 目标代码的大小(object size),编译得到的目标代码当然是越精悍越好了。

    3. 目标代码运行时间(run time),运行时间体现了速度和效率。

    这里,作者没有亲自测试和实验,引用了Scott Robert Ladd的《GCC 4.0: A Review for AMD and Intel Processors》文章中的一些实验结果。这篇文章引起了比较大的反响,其实验结果和结论也得到了广泛的认可,如果对Scott的具体测试采用的软硬件平台和工具方法感兴趣,原文可以在http://www.coyotegulch.com/reviews/gcc4/index.html看到。

    Scott使用AMD和Intel的两款处理器:64位的Opteron处理器和32位的Pentium 4处理器,分别针对GCC 3.4.3和GCC 4.0来进行测试。他选用了POV-Ray 3.6.1, LAME 3.96.1, SciMark 2.0和Linux 2.6.11.8作为benchmark来进行测试,分别记录了GCC 3.4.3和GCC 4.0在ADM Opteron和Intel Pentium 4下的编译时间、代码大小和代码运行时间进行比较,具体的实验结果请参见原文。

    这样,根据这些实验数据,我们可以给出一个粗糙的结论,在编译性能方面,GCC 4.0似乎不如GCC 3.4.3,因为在很多时候,GCC 4.0的编译时间、代码大小以及代码运行时间全面高于GCC 3.4.3。这样的结果看似出人意料,GCC这次大的版本变化就是因为引入了新的优化框架,怎么会编译性能有所下降呢?这主要是因为:首先,这是一次主版本变化,我们可以理解巨大的变化带来的性能损耗;另外,更主要的是,GCC新的优化框架的潜力尚未完全发挥出来,这一点,在我们文章结束的时候,读者会有更深的理解。
    3. GCC 4.0 的内部结构变化

    GCC 遵循 GPL 协议,是开放源代码的,其开发过程也是完全开放的,任何人都可以对 GCC 的发展作出贡献,因而 GCC 特别适合用于学习和研究编译器。对于学习和研究编译器本身来说,GCC 内部的结构变化显然更吸引人。

    目前 GCC 发行维护者 Mark Mitchell 在接受 internetnews.com 网站采访时这样说到:"毫无疑问地,GCC 4.0 中最引人注意的特性以及为何把 GCC 的这个版本称作 4.0 而不是 3.5,就是因为其新的优化框架(optimization infrastructure)。大体上来说,GCC 以前的版本的代码优化工作主要在底层机器指令级别进行的。不幸的是,到了底层的时候,很多信息已经丢失了,因此,GCC 4.0 在更接近输入高级语言程序的级别上做了很多优化工作。"

    Mark Mitchell 所说的 GCC 新的优化框架主要就是指 Tree SSA(Static Single Assignment),Tree SSA 经过长时间的独立开发,最终整合进了 GCC 的主流(mainstream)中,可见这种设计是意义非凡的。Tree SSA 是什么?为什么要采用 Tree SSA? 使用了 Tree SSA 的 GCC 有什么不同?新的 GCC 编译和优化框架是什么样的?等等,这些将是本文探讨的主要问题。

    这部分文章内容是这样组织的:首先回顾 GCC 4.0 版本之前的编译流程,以便进行对比;接下来介绍 GCC 4.0 的编译流程以及新的优化框架结构,这里先介绍 GENERIC Tree和 GIMPLE Tree,SSA 形式等基本概念,在读者对这些概念和理论有了一定的了解之后,再介绍 GCC 中是如何实现 Tree SSA 框架结构的。

    3.1 GCC 4.0 之前的编译流程

    这里有必要回顾一下 GCC4.0 之前的版本进行代码优化的框架结构,以便进行对比分析。GCC 的前端在接受了输入的源程序之后,经过分析器(parser)处理得到 Parse Tree(通常是一种抽象语法数,AST, Abstract Syntax Tree),根据这个 Parse Tree 生成程序的RTL(Register Transfer Language)表示,然后在 RTL 表示的基础上进行优化处理,然后生成相应的目标代码,如下图 1 所示。

    但是 RTL表示是一个相当接近底层的表示,也就是说它更接近目标代码,适合进行目标相关的优化工作,比如寄存器分配等等。然而,很多的优化转换工作需要更高层的程序信息,比如数组引用、数据类型、控制流结构等等,这些很难用 RTL 表示,或者无法用 RTL表示。


    图1 GCC 4.0之前版本的代码编译流程和优化框架

    3.2 GCC 4.0 的优化框架(Optimization Infrastructure)

    提供一个可移植性强、跨平台以及编译高效代码的编译器,是 GCC 一贯追求的目标,为了使 GCC 能够获得更好的编译性能,高层程序信息级别的优化工作是必须的。Tree SSA设计的主要目的就在于此,它既与前端语言无关,又与后端目标无关,而且能够提供在 RTL表示层很难或者无法进行的高级分析和转换。

    Tree SSA 起先是作为 GCC 的一个分支(branch)进行独立开发的,经过两年多的努力开发,终于在 2004 年 5 月 13 日进入了 GCC 的主流版本。在 GCC 的 SSA for Trees 分支的网页上明确说明了,这个项目的目的就是构建一个对基于 SSA 形式的树的优化框架。在学习编译原理的时候我们知道,编译器通常会使用树的形式来描述程序,GCC 也是这样,在接受了输入的源程序后,GCC 驱动其相应语言的前端分析器(parser),处理得到一个 Tree。从图 1 可以看到,4.0 版本之前的 GCC 几乎是立即把这些 Tree 转换成了 RTL 表示。那么现在 GCC4.0 的 Tree SSA 优化框架就是在前端生成 Parse Tree 之后,把这些 Tree 转换成基于 SSA 的 Tree,对这些 SSA 形式的 Tree 进行高层次的优化,然后才把 Tree 转换成 RTL 表示。

    3.2.1 GENERIC Tree 和 GIMPLE Tree

    这个框架结构看起来比较简单,而且 GCC 的 Parse Tree 能够提供足够的信息来实现SSA,但是在真是实施的时候,还是有很大的困难的,最主要的两个困难是这样的:

    1. GCC 中的树没有统一的表示形式,每一个前端都定义了自己的树。这就意味着要得到基于SSA形式的树必须要对每种前端分析生成的树都进行处理。

    2. GCC 前端得到的 Parse Tree 的复杂度是无法估量的。把这些树转换成基于 SSA形式的树需要进行复杂的处理工作。

    为了解决以上两个问题,GCC Tree SSA 分支的开发小组引入了 GENERIC Tree 和GIMPLE Tree 两个概念:

    1. GENERIC Tree 是特意创造出来的 GCC通用的树的表示形式,它能够表示不同的前端所需要的所有的结构,而且又能够去除语言相关性。

    2. GIMPLE Tree是取自GENERIC Tree和SIMPLE两个短语的。因为GENERIC Tree的复杂性导致实现SSA形式的困难,需要把GENERIC Tree进行简化,这种简化的GENERIC Tree就称之为GIMPLE Tree。

    好了,了解了这些内容之后,我们可以看看 GCC 4.0 的编译流程和优化框架,如下图2所示:


    图2 GCC 4.0 的编译流程和优化框架

    3.2.2 Single Static Assignment Form 的基础介绍

    到现在为止,我们基本搞清了新的 GCC 的编译过程,也大概了解了所谓的新的 Tree SSA 优化框架。上面提到的 GENERIC Tree 和 GIMPLE Tree 都是为了实现 SSA 而做的准备工作,那么 SSA 本身究竟是什么?为什么 GCC 要把 Parse Tree 转换成基于 SSA 形式的 Tree 再做优化工作呢?为弄清楚这些问题,我们有必要多花一些篇幅对SSA的基本概念和理论做一些介绍。

    SSA 的全称是 Static Single Assignment,直译过来就是静态单一赋值,它是IBM公司在上个世纪 80 年代研究的成果。从前面的讨论可以看出,Tree SSA 与 RTL 一样,也是一种中间表示形式,不过相比 RTL 要更高层一点。SSA 形式是一种相对而言比较新颖的中间表示形式,早期的讲编译原理或者编译器的课本中大多没有提及。

    简单的说,SSA 形式就是每个变量只能被赋值一次。这样,非 SSA 形式的程序在转换成 SSA 形式的时候,其中的部分变量就会被分割成很多版本,通常使用下标来表示这些不同的版本。下面举一个简单的例子来说明,如下图 3 所示:


    图3 程序的非 SSA 形式和 SSA 形式

    上图中所示的代码片段,由于 y 变量被赋值两次,所以在转化成 SSA 形式的时候,y变量被分割成两个版本 y1 和 y2,这样就保证了每个变量仅仅被赋值一次。

    由于 SSA 形式中每个变量只能被赋值一次,那么 SSA 形式就能有效地把程序中所操作的数值和这些数值的存储位置这两者分开,这样就能方便一些优化工作。比如我们刚才看的图 3 中的代码片段,我们可以通过肉眼分析发现在非 SSA 形式中的第一条语句y := 1是一条无效的冗余语句,真正决定 y 变量值的是第二条 y := 2 赋值语句。那么在代码优化的时候,第一条语句就应该被删除掉。但是这是我们人工发现的优化结果,如果想要编译器来完成这个优化工作,需要进行复杂的分析,在编译原理中称之为"定义可达性分析"(reaching definition analysis)。而在 SSA 形式中,显然,做出这样的优化决定则无需进行太多分析。

    这只是 SSA 形式诸多优点中的一个而已,使用 SSA 形式可以利用更多的编译器优化算法或者是提高这些算法的效率,比如 constant propagation, sparse conditional constant propagation, dead code elimination, global value numbering, partial redundancy elimination 以及register allocation 等等。这里涉及太多编译理论和算法,本文不作详细讨论。

    SSA 形式具有上文所述的优点,当然也会有其复杂和困难的地方了,下面我们通过一个稍微复杂点的程序片段来说明把非 SSA 形式的程序转换成 SSA 形式程序的时候有些什么需要考虑的问题:


    图4

    图 4-a 所示的非 SSA 形式的程序在转换成了图 4-b 所示的 SSA 形式的程序后,有一个问题很难解决,就是图 4-b 中最下面程序块中 y 的值无法确定。因为在此之前有一条选择语句,导致程序控制流产生了分支,此时 y 的值可能是 y1 也可能是 y2,这是由程序具体执行时经过哪一条控制流来决定的。处理的方法是在最后一块程序片段之前加上一个 Φ 函数,定义一个新的 y3,并从 y1 或者 y2 中选择一个适当的值赋给 y3,如图 4-c 所示。

    推而广之,在将非 SSA 形式的程序转换成 SSA 形式后,如果某个变量被分割成了 n个不同版本的变量后,在某一点需要会合,那么在这个会合点 (joint point) 就需要加入一个 Φ 函数来确定应该选择这 n 个不同版本的变量中的某一个值。这样问题就转变成了如何计算这个 Φ 函数以及确定在程序中的什么位置应该插入 Φ 函数,这个可以使用 dominance frontiers 来进行计算,关于如何高效计算这个 Φ 函数的算法研究本文不作深入讨论。
    3.2.3 GCC 4.0 中的 Tree SSA 框架的设计和实现

    至此,我们已经比较清楚的了解了什么是 SSA,SSA 形式有什么好处,如何将非 SSA表示转换成基于SSA形式的表示以及这个过程中需要解决的主要问题等等,本文将不再对SSA进行深入的讨论了,回到我们的主题,继续讨论GCC 4.0 的 Tree SSA 优化框架是如何设计和实现的,它是怎样把 Tree SSA 融入到 GCC 的编译过程中去的。

    回顾上图2表示的 GCC 4.0 编译流程,在得到 GIMPLE Tree 之后,经过 GIMPLE optimizer 和 GIMPLE expander 的处理,得到了程序的 RTL 表示。其实把程序转换成SSA形式和在此基础上进行优化就在这个过程中完成的,下面我们就来具体看看 GCC 4.0 是怎么做的。在 GIMPLE 和 RTL 之间是树优化 (Tree Optimization) 的过程,如下图 5 所示。图 5 中所示的树优化器 (Tree Optimizer) 主要完成这几个功能:

    1. 生成一个控制流转换图 CFG(Control Flow Graph)。

    2. 将 GIMPLE Tree 转换成基于 SSA 形式的 Tree。

    3. 进行 Tree SSA 的优化。

    4. 将优化后的基于 SSA 形式的 Tree 转换成 RTL 接口所能识别的非 SSA Tree 的形式。


    图5 GCC 4.0 的 Tree Optimizer 结构

    结合 GCC 4.0 代码中的部分源文件来说明:图 5 中这个 Tree Optimizer 的行为是由tree-optimize.c 文件驱动的,tree-ssa.c, tree-into-ssa.c 以及 tree-outof-ssa.c 负责完成 SSA 形式的转换、验证以及其他需要与 SSA 形式交互的功能,建立和管理控制流转换图 CFG 的工作由 tree-cfg.c 完成。不同的树分析和优化功能分别在相应的源文件中独立实现,这些分析和优化函数必须向 GCC 注册,然后才能由 tree-optimize.c 进行驱动和管理,结合图 5来看,SSA pass1, SSA pass2 … SSA passn 代表了实现各种不同功能的 SSA 分析器,他们向 Tree Optimizer 注册,在编译时刻 Tree Optimizer 根据编译选项等等选择相应的SSA分析器进行优化或者其他处理工作,并保证在基于 SSA 形式的处理完成之后将 SSA 树转换成非 SSA树,交给下面的 RTL 模块处理。

    至此,GCC 4.0 的 Tree SSA 优化框架结构应该比较清楚了。读者可能会注意到,文中多次提到Tree SSA是一个优化框架结构(Optimization Infrastructure),为什么说是一个Infrastructure 呢?其实把 Infrastructure 称作框架结构或许并不贴切,更精确地说 Tree SSA是 GCC 提供的一种进行优化工作的基础设施。图 5 中已经体现出了这一点,Tree SSA 的分析和优化工作不是一成不变的,具体选择哪些优化功能和算法是由Tree Optimizer选择驱动的,而且这个Infrastructure是相当灵活的,我们可以很方便的加入一个 SSA 分析器,只需要如下步骤:

    1. 创建一个 struct tree_opt_pass 类型的全局变量。

    2. 在 tree-pass.h 头文件中为这个新的分析器添加一个外部声明(extern declaration)。

    3. 调用 NEXT_PASS 把这个新的分析器加入到 tree-optimize.c: init_tree_optimization_passes 序列中。

    所以这种 Infrastructure 是一个相当灵活和方便的设计,使得我们可以便利地加入新的SSA分析器,或者使用更高效的算法来重新设计完成一些优化功能等等。

    4. 总结

    GCC 4.0 发布以来得到了引起了广泛的关注,新的 gfortran 前端给 Fortran 程序员带来了福音,但也有很多不尽如人意的地方,比如编译性能的损耗,以及向后兼容问题。比如KDE(K Desktop Environment) 开发小组在发现 GCC 4.0 无法正常编译 KDE 后,迅速的抛弃了 GCC 4.0。而且,出于稳定性的考虑,GCC 3.x 版本的开发仍然在进行中。这些似乎给 GCC 4.0 的前景笼罩上了一层阴影,但我们应该容忍一点,给 GCC 4.0 一些信心,向后兼容的问题应该是能够解决的。

    至于编译性能的问题,通过我们这篇文章的讨论,我们可以看到 GCC 4.0 Tree SSA 的优化框架结构并没有充分发挥出其潜能,还可以增加和改进更多 Tree SSA 的优化工作,假以时日,GCC 4.0 的编译性能会得到提高的。

    总的来说,这次 GCC 从 3.x 版本跃迁到 4.x 版本更像一个进化 (evolution) 的过程,而非一个革命性 (revolution) 的过程,它采用的 Tree SSA 优化框架代表了编译器发展的方向,我们应该关注 GCC 4.0 的进一步发展。

    5. 参考文献

    [1] Tree SSA: A New Optimization Infrastructure for GCC. Diego Novillo, Red Hat Canada, Ltd.发表在 GCC Developers Summit 上。

    [2] Kenneth Zadeck在GCC&GNU Toolchain Developers' Summit 2004 上做的关于 SSA 的报告,Static Single Assignment Form。

    [3] Design and Implementation of Tree SSA. 仍然是 Diego Novillo 在 GCC Developers Summit 上发表的文章。

    [4] Scott Robert Ladd 的《GCC 4.0: A Review for AMD and Intel Processors》,对 GCC 4.0 和GCC 3.4.3 的性能做了评测,http://www.coyotegulch.com/reviews/gcc4/index.html。

    [5] GNU 主页上关于 GCC Tree SSA branch 的说明,http://gcc.gnu.org/projects/tree-ssa/。

    关于作者

    王逸,南京大学计算机科学与技术系在读博士生,对软件安全、Linux、编译器等比较有兴趣,目前主要关注的是隐蔽信道分析。
    08/08/2005

    什么是Cairo?

    Cairo,过去叫做Xr或Xr/Xc,是一个跨平台的开放源代码的矢量图形函数库,可以提供高质量的显示和打印输出。通过Glitz函数库, Cairo 能使用 OpenGL或X Render扩展的硬件加速功能来绘制图像,这使得基于Cairo的应用能在现代化的3D显示硬件上获得益处。

    Cairo提供一个稳定的用户层API,它可以提供现代化的图形处理管理能力,如绘制和填充,映射转换,合成(注意,是合成)与改变alpha半透明图像,高真文本显示等等。能够在不同的媒体上实现相同的输出。

    其实更早关于Cairo人们关注的还是Gtk/Gnome的进展,我们可以在 Gnome中国 上看到对Cairo应用前景的展望,可以看到,Cairo应用可以实现的东西是激动人心的。

    Cairo拥有多种不同的后端,能够支持多种输出设备。现在所支持的后端包括:

    图像:以内存图像缓冲区(in-memory image buffers)为目标。该图像缓冲区可被保存成文件,或者其数据可以被不具有本地后端的图形系统调用。

    gl: 通过 glitz库(http://www.freedesktop.org/Software…制图像。包括GLX 和 AGL (分别为Unix和苹果的标准)。

    png: 这个后端使用图像后端来生成png图像文件。

    ps: 生成一个PostScript文件,适合高质量打印输出。现在ps后端生成点阵内容,连接图像后端。

    xlib: 使用X Window的xlib接口,以Windows 或 Pixmaps 为目标。Render扩展可用,但不必需。

    xcb: 和xlib相似,但使用XCB(http://freedesktop.org/Software/xcb)接口。

    还有其他一些后端正在开发中:

    pdf

    svg

    quartz——MacOS的后端(http://cairographics.org/QuartzBackend

    win32——Windows GDI 后端(http://cairographics.org/win32

    16/07/2005

    跟我一步一步学汉化(一)

    如何使Linux桌面正确显示和输入中文, 一直是令Linux初学者头痛的事情. 目前有很多汉化的文章, 但大多只讲步骤而不谈道理, 让初学者云里来雾里去. 这篇文章将从X的字体原理入手,帮助大家理解X的字体机制,在汉化中学习.

    首先, 我们要理解X的字体引擎. X中支持Truetype字体(一种可放缩字体) 的主要有四种引擎. freetype, xtt, xfs和Xft.

    其中freetype 和 xtt 是 X 的内部模块. 使用的都是freetype1渲染引擎.

    Xfs 和xft 是外部服务程序. xfs 是系统级的字体服务程序, 也可以作为X的内部模块, 使用的是X 内部的freetype2. Xft 和其他类库一样, 只有被调用的时候才被加载. 其中只有xft才有antialias 支持.

    这里有必要讲讲freetype. Freetype 是开源字体渲染引擎, 并不只为X设计. 它的功能就是读取Truetype字体信息, 如大小, 分辨率, 编码等, 然后渲染成所需的位图数据输出. Freetype 现在的版本是 2.x, 与1.0 相比, 最大的差别就是加入了抗锯齿功能.

    有这么多引擎,到底要用哪个好? 其实我们目前为止, 支持中文最好的还是xtt. 因为小字体的时候, 用函数描述法算出来的中文字体效果不能让人满意, 所以很多中文字体公司就在Truetype字体里嵌入了位图字体. 这些位图字体需要用特殊的方式读出来, 所有的引擎中就只有xtt能做到这一点.

    我们下一步就是将字体添加到X中, 使xtt可以正确读取, 这样就可以了.

    首先, 由于要配置Linux系统文件, 我们需要用root帐号进入, 相当于windows下的administrator.

    X的配置文件是/etc/XF86Config-4 (比较新的显卡) 或者是 XF86Config (比较老的显卡). 用你喜欢的文本编辑器打开, 如 kedit或gedit.

    打开后我们发现配置文件分成很多个Section, 我们首先要配置的就是 Section "Files" , 这个部分描述了X所要调用的文件信息.

    要加入一个字体目录, 只需在里面插入一行 FontPath "目录名" 就可以了, 如:

    Section "Files"
    # Multiple FontPath entries are allowed (they are concatenated together)
    # By default, Mandrake 6.0 and later now use a font server independent of
    # the X server to render fonts.
    FontPath "unix/:-1"
    FontPath "/truetype" # 插入一个字体目录
    EndSection

    然后, 我们让X加载xtt字体引擎:

    找到Section "Module", 像这样修改:

    Section "Module"
    # Load "dbe" # Double-Buffering Extension
    # Load "v4l" # Video for Linux
    Load "extmod"
    Load "glx
    # Load "type1" # type1 模组是渲染type1字体的, 和xtt冲突, 必需屏蔽
    # Load "freetype" # freetype 模组是渲染Truetype字体的, 和xtt冲突, 必需屏蔽
    Load "xtt" # 加入xtt模组引擎
    EndSection

    好了, 现在xtt会自动去 /truetype 里找字体.

    现在开始拷贝字体到 /truetype里去, 先要在根目录建一个truetype目录, 打

    mkdir /truetype

    就可以了.

    从windows分区拷贝要先mount, 就是作一个联接, 将Linux目录连到windows分区. 在根目录下建一个"c"目录.

    mkdir /c

    然后

    mount /dev/hda1 /c

    这样就将windows下的C盘 联接到我们Linux下的/c目录了, 进入/c, 应该可以看到你C盘的文件.

    然后, 进入/c 中的字体文件目录, 一般在window下面的Fonts里, 注意目录名大小写在Linux下面是有区别的.

    拷贝字体文件到 /truetype里, 打

    cp simsun.ttc /truetype/simsun.ttf
    cp tahoma* /truetype/

    这样就将我们所需要的字体文件拷贝到truetype 里了.

    下一步我们要设置字体文件, Linux中X的字体设置很烦锁, 不像windows一拷贝就完事, 初级阶段嘛, 大家还是忍忍吧, 呵呵.

    1. 建立字体信息文件fonts.dir

    如下

    24
    simsun.ttf -misc-SimSun-medium-r-normal--0-0-0-0-c-0-gb2312.1980-0
    ai=0.3:simsun.ttf -misc-SimSun-medium-i-normal--0-0-0-0-c-0-gb2312.1980-0
    ds=y:simsun.ttf -misc-SimSun-bold-r-normal--0-0-0-0-c-0-gb2312.1980-0
    ds=y:ai=0.3:simsun.ttf -misc-SimSun-bold-i-normal--0-0-0-0-c-0-gb2312.1980-0
    tahoma.ttf -misc-SimSun-medium-r-normal--0-0-0-0-p-0-iso8859-1
    ai=0.3:tahoma.ttf -misc-SimSun-medium-i-normal--0-0-0-0-p-0-iso8859-1
    tahomabd.ttf -misc-SimSun-bold-r-normal--0-0-0-0-p-0-iso8859-1
    ai=0.3:tahomabd.ttf -misc-SimSun-bold-i-normal--0-0-0-0-p-0-iso8859-1
    simsun.ttf -misc-SimSun-medium-r-normal--0-0-0-0-p-0-gbk-0
    ai=0.3:simsun.ttf -misc-SimSun-medium-i-normal--0-0-0-0-p-0-gbk-0
    ds=y:simsun.ttf -misc-SimSun-bold-r-normal--0-0-0-0-p-0-gbk-0
    ds=y:ai=0.3:simsun.ttf -misc-SimSun-bold-i-normal--0-0-0-0-p-0-gbk-0
    simsun.ttf -misc-SimSun-medium-r-normal--0-0-0-0-p-0-fcd8859-15
    ai=0.3:simsun.ttf -misc-SimSun-medium-i-normal--0-0-0-0-p-0-fcd8859-15
    ds=y:simsun.ttf -misc-SimSun-bold-r-normal--0-0-0-0-p-0-fcd8859-15
    ds=y:ai=0.3:simsun.ttf -misc-SimSun-bold-i-normal--0-0-0-0-p-0-fcd8859-15
    simsun.ttf -misc-SimSun-medium-r-normal--0-0-0-0-p-0-iso8859-15
    ai=0.3:simsun.ttf -misc-SimSun-medium-i-normal--0-0-0-0-p-0-iso8859-15
    ds=y:simsun.ttf -misc-SimSun-bold-r-normal--0-0-0-0-p-0-iso8859-15
    ds=y:ai=0.3:simsun.ttf -misc-SimSun-bold-i-normal--0-0-0-0-p-0-iso8859-15
    simsun.ttf -misc-SimSun-medium-r-normal--0-0-0-0-c-0-iso10646-1
    ai=0.3:simsun.ttf -misc-SimSun-medium-i-normal--0-0-0-0-c-0-iso10646-1
    ds=y:simsun.ttf -misc-SimSun-bold-r-normal--0-0-0-0-c-0-iso10646-1
    ds=y:ai=0.3:simsun.ttf -misc-SimSun-bold-i-normal--0-0-0-0-c-0-iso10646-1



    第一行的24表示下面一共有24行设置 (好像有点傻) , 其他的格式都差不多:

    ds=y:ai=0.3:simsun.ttf -misc-SimSun-bold-i-normal--0-0-0-0-p-0-gbk-0

    说明:

    simsun.ttf:
    字体文件名

    ds=[yn]: ds是xtt的功能, 设成"y" 表示粗体, "n" 表示正常.

    ai=Real_number: 表示倾斜度. 不设表示自动.

    misc :
    表示字体的类别

    SimSun :
    是字体的名称

    bold : bold 表示粗体, 其他如medium表示正常,

    i : 表示斜体, r 是正常

    p: 可变长度, c 是正方形, m是固定宽度

    gbk: 字体编码

    这里大家发现我们还使用了tahoma英文字体, 这样替换, 系统读取Simsun英文字体的时候, 就会用pp的tahoma替代.

    好了, 现在存盘. 然后拷贝一个到fonts.scale

    cp fonts.dir fonts.scale

    然后拷贝编码文件 encodings.dir 到目录里来.

    cp /usr/X11R6/lib/X11/fonts/encodings/encodings.dir /truetype

    好, 重起, 把KDE, mozilla, galeon 等等所有默认字体都改成simsun, 哈哈, 是不是比从前漂亮了很多?

    大家还是把这几个配置文件备份起来, 以后安装的时候就不用再设置了.

    这里顺带说一下其他几个字体引擎的配置:

    xfs : 配置文件是 /etc/X11/fs/config
    xft : 配置文件是 /etc/X11/Xftconfig

    xft 的配置相对来说比较容易, 只要将字体拷到配置文件中dir 指定的任何一个目录就可以了. 如果要小字体不显示AA, 可以在末尾加入:

    match
    any size > 8
    any size < 17
    edit
    antialias = false;
    match
    any pixelsize > 8
    any pixelsize < 17
    edit
    antialias = false;


    这样 8~17号的字体就不会用抗锯齿功能了.