5. 各种主题

5.1. 内存类型范围寄存器

从奔腾级处理器开始,包括 Athlon、K6-2 和其他 CPU,都存在内存类型范围寄存器 (MTRR),它们控制处理器如何访问内存位置范围。 基本上,它将对显卡的许多较小的单独写入转换为单个写入(突发)。 这提高了写入显卡的效率,并可以将图形速度提高 250% 或更多。

参见/usr/src/linux/Documentation/mtrr.txt了解详情。 请注意,自从编写此文件以来,XFree86 已被打上补丁,可以自动检测您的显卡 RAM 基地址和大小并设置 MTRR。

5.2. 为了充分发挥系统的性能

5.3. 关于 Linux 上的库

您在游戏中会看到的常见问题是找不到库文件。 它们有点神秘,并且有奇怪的名称,因此我们将简要介绍一下 Linux 上的库。 库有两种类型:静态库和动态库。 当您编译程序时,默认情况下,gcc 使用动态库,但是您可以使用-static开关使 gcc 改为使用静态库。 除非您计划从源代码编译游戏,否则您主要会对动态库感兴趣。

5.3.1. 动态库

动态库,也称为“共享库”,在应用程序运行时为其提供目标代码。 也就是说,代码在运行时(而不是编译时)链接到可执行文件中。 它们类似于 Windows 使用的.dll。“动态”链接代码的程序称为 /etc/ld.so,动态库本身通常以.so结尾,并带有版本号,例如

    /usr/lib/libSDL.so
    /lib/libm.so.3
			

当使用 gcc 时,您可以通过删除字符串来引用这些库lib, .so和所有版本号。 因此,要使用这两个库,您需要向 gcc 传递-lSDL -lm选项。 然后,gcc 将“在可执行文件中放置一个备忘录”,指示在/usr/lib/libSDL.so/lib/libm.so.3文件中查找,每当使用 SDL 或数学函数时。

5.3.2. 静态库

与在应用程序运行时提供代码的动态库相比,静态库包含在编译程序时链接(插入)到程序中的代码。 在运行时不会插入任何代码; 代码是完全独立的。 静态库通常以.a结尾,后跟版本号,例如

    /usr/lib/libSDL.a
    /usr/lib/libm.a
			

这些.a文件实际上是许多.o(目标)文件归档在一起的档案,类似于 tar 文件。 您可以使用 nm 查看静态库包含哪些函数

    % nm /usr/lib/libm.a
    ...
    e_atan2.o:
    00000000 T __ieee754_atan2
    
    e_atanh.o:
    00000000 T __ieee754_atanh
    00000000 r half
    00000010 r limit
    00000018 r ln2_2
    ...
			

当使用 gcc 时,您可以通过删除字符串“lib”、“.a”和所有版本号来引用这些库。 因此,要使用这两个库,您需要向 gcc 传递-lSDL -lm选项。 然后,gcc 将在编译过程中看到数学函数时,从/usr/lib/SDL.a/usr/lib/libm.a“焊接到”代码中。

5.3.3. 如何查找库文件

如果您编译自己的游戏,您在库方面最大的问题要么是 gcc 找不到静态库,要么是您的系统上可能不存在该库。 当玩二进制游戏时,您在库方面的困境要么是 ld.so 找不到库,要么是您的系统上不存在该库。 因此,谈论 gccld.so 最初是如何查找库的,是有意义的。

gcc 在“标准系统目录”以及您使用-L选项指定的任何目录中查找库。 您可以使用 gcc -print-search-dirs 找到这些标准系统目录是什么

ld.so 查找名为/etc/ld.so.cache的文件中包含的二进制哈希,以获取包含可用动态库的目录列表。 由于它包含二进制数据,因此您无法直接修改此文件。 但是,该文件是从文本文件/etc/ld.so.conf生成的,您可以编辑该文件。 此文件包含您希望 ld.so 搜索动态库的目录列表。 如果您想开始将动态库放在/home/joecool/privatelibs中,您需要将此目录添加到/etc/ld.so.conf。 您的更改实际上不会进入/etc/ld.so.cache,直到您运行 ldconfig; 一旦运行,ld.so 将开始在您的私有目录中查找库。

此外,即使您只是向系统中添加了额外的库,也必须更新ld.so.cache以反映新库的存在。

5.3.4. 找出游戏依赖哪些库

大多数商业 Linux 游戏将动态链接到各种 LGPL 库,例如 OpenAL 或 SDL。 在这些示例中,将使用 Bioware 的《无冬之夜》<http://nwn.bioware.com>。

要找出游戏使用了哪些库,我们可以使用 “ldd” 命令。 Cd 到/usr/games/nwn,或您安装它的任何位置,并查看文件。 您应该看到一个名为nwmain的文件; 这是实际的游戏二进制文件。 输入 “ldd nwmain”,您将看到

   $ ldd nwmain
       linux-gate.so.1 =>  (0xffffe000)
       libm.so.6 => /lib/libm.so.6 (0x40027000)
       libpthread.so.0 => /lib/libpthread.so.0 (0x40049000)
       libGL.so.1 => /usr/lib/libGL.so.1 (0x4009b000)
       libGLU.so.1 => /usr/X11R6/lib/libGLU.so.1 (0x40103000)
       libmss.so.6 => not found
       libSDL-1.2.so.0 => /usr/lib/libSDL-1.2.so.0 (0x40178000)
       libc.so.6 => /lib/libc.so.6 (0x401ff000)
       /lib/ld-linux.so.2 (0x40000000)
       libGLcore.so.1 => /usr/lib/libGLcore.so.1 (0x40319000)
       libnvidia-tls.so.1 => /usr/lib/libnvidia-tls.so.1 (0x409f1000)
       libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x409f3000)
       libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x40a01000)
       libdl.so.2 => /lib/libdl.so.2 (0x40acd000)
       libstdc++.so.5 => /usr/lib/libstdc++.so.5 (0x40ad1000)
       libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x40b88000)
       libasound.so.2 => /usr/lib/./libasound.so.2 (0x40b90000)
			

ldd 显示动态可执行文件依赖的所有库,并显示它们的位置。 它还“拉入”依赖项的依赖项。 例如,虽然 NWN 本身不依赖于libnvidia-tls.so,但我系统上 Nvidia 提供的 libGL 依赖于它。

缺少库?

在上面的示例中,我们可以看到nwmain想要libmss.so.6,但链接器找不到它。 通常,缺少库是等待发生的崩溃。 但还有一件事需要考虑:大多数游戏实际上是由“包装器”(shell 脚本)启动的,该脚本在启动游戏之前执行一些神奇的操作。 在 NWN 的情况下,包装器称为nwn。 现在让我们看一下它

   $ less nwn
   #!/bin/sh
   
   # This script runs Neverwinter Nights from the current directory
   
   export SDL_MOUSE_RELATIVE=0
   export SDL_VIDEO_X11_DGAMOUSE=0
   
   # If you do not wish to use the SDL library included in the package, remove
   # ./lib from LD_LIBRARY_PATH
   export LD_LIBRARY_PATH=./lib:./miles:$LD_LIBRARY_PATH
   
   ./nwmain $@
			

此脚本设置了一些环境变量,然后使用我们添加的任何命令行选项启动游戏二进制文件。 这里相关的部分是名为 “LD_LIBRARY_PATH” 的环境变量。 这是一种添加到链接器搜索路径的方法。 尝试将该行复制到您的 shell 中,看看重新运行 ldd 时会发生什么。

   $ export LD_LIBRARY_PATH=./lib:./miles:$LD_LIBRARY_PATH
   $ ldd nwmain
       linux-gate.so.1 =>  (0xffffe000)
       libm.so.6 => /lib/libm.so.6 (0x40027000)
       libpthread.so.0 => /lib/libpthread.so.0 (0x40049000)
       libGL.so.1 => /usr/lib/libGL.so.1 (0x4009b000)
       libGLU.so.1 => /usr/X11R6/lib/libGLU.so.1 (0x40103000)
       libmss.so.6 => ./miles/libmss.so.6 (0x40178000)
       libSDL-1.2.so.0 => ./lib/libSDL-1.2.so.0 (0x401ec000)
       libc.so.6 => /lib/libc.so.6 (0x4025e000)
       /lib/ld-linux.so.2 (0x40000000)
       libGLcore.so.1 => /usr/lib/libGLcore.so.1 (0x40378000)
       libnvidia-tls.so.1 => /usr/lib/libnvidia-tls.so.1 (0x40a50000)
       libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x40a52000)
       libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x40a60000)
       libdl.so.2 => /lib/libdl.so.2 (0x40b2c000)
       libstdc++.so.5 => /usr/lib/libstdc++.so.5 (0x40b30000)
       libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x40be7000)
			

如您所见,这给了我们略有不同的结果。 NWN 库目录已添加到搜索路径的前面,因此现在链接器可以在 “libmss.so.6中找到./miles” 目录中找到,并且首先找到本地副本 libSDL,不再使用系统副本。

这些脚本还有另一个好处:它们很容易编辑,允许您提供自己的库副本。 任何游戏提供的库副本(例如 OpenAL 或 SDL)都可能针对最低公分母进行编译,可能是 i486 或 i686。 如果您有 Pentium4 或 AthlonXP,您可以专门为您的处理器编译自己的版本。 编译器将尝试优化生成的二进制文件,从而提高一些性能。 有关此方面的更多信息,请访问 GCC 网站

使 NWN 使用您的系统副本很容易。 它在包装器脚本中说明了! 从LD_LIBRARY_PATH行中删除 “./lib:”,您就可以开始了。

另一个不错的小技巧是针对使用 OpenAL 进行声音输出的游戏(例如,基于 Unreal 的游戏:UT、Postal、Rune 等)。 由于 Open Sound System (OSS) 已被弃用,转而支持 ALSA,因此我所见过的所有 Linux 发行版现在都默认提供 ALSA 支持,而 OSS 支持实际上是通过 ALSA 的兼容性模块提供的。 随游戏分发的openal.so副本通常不*支持 ALSA,因此使游戏使用您自己编译的副本将允许您原生使用 ALSA。