0
在Windows下安装软件时,只需运行软件的安装程序(setup、install等)或者用zip等解压缩软件解开即可安装,运行反安装程序(uninstall、unware、“卸载”等)就能将软件清除干净,完全图形化的操作界面,简单到只要用鼠标一直点击“下一步”就可以了。而Linux好象就不一样了,很多的初学者都抱怨在Linux下安装和卸载软件非常地困难,没有像使用Windows时那么直观。其实在Linux下安装和卸载软件也非常简单,同样也有安装向导或解压安装的方式,不相同的只不过是除了二进制形式的软件分发外,还有许许多多以源代码形式分发的软件包,下面就来详细地讲一讲这些软件的安装与卸载:
一、二进制分发软件包的安装与卸载
Linux软件的二进制分发是指事先已经编译好二进制形式的软件包的发布形式,其优点是安装使用容易,缺点则是缺乏灵活性,如果该软件包是为特定的硬件/操作系统平台编译的,那它就不能在另外的平台或环境下正确执行。
1、*.rpm形式的二进制软件包
安装:rpm -ivh *.rpm
卸载:rpm -e packgename
说明:RPM(RedHat Packge Manager)是RedHat公司出的软件包管理器,使用它可以很容易地对rpm形式的软件包进行安装、升级、卸载、验证、查询等操作,安装简单,而卸载时也可以将软件安装在多处目录中的文件删除干净,因此推荐初学者尽可能使用rpm形式的软件包。rpm的参数中-i是安装,-v是校验,-h是用散列符显示安装进度,*.rpm是软件包的文件名(这里的*.rpm特指*.src.rpm以外的以rpm为后缀的文件);参数-e是删除软件包,packgename是软件包名,与软件包的文件名有所区别,它往往是文件名中位于版本号前面的字符串,例如apache-3.1.12-i386.rpm和apache-devel-3.1.12-i386.rpm是软件包文件名,它们的软件包名称分别是apache和apache-devel。更多的rpm参数请自行参看手册页:man rpm。
如果你不喜欢在字符界面下安装或卸载这些软件包,完全可以在X-Window下使用图形界面的软件包管理程序,如glint、xrpm这样的图形接口,或者是KDE的kpackge等,这样对软件包的安装、升级、卸载、验证和查询就可以通过点击鼠标来轻松完成。
2、*.tar.gz/*.tgz、*.bz2形式的二进制软件包
安装:tar zxvf *.tar.gz 或 tar yxvf *.bz2
卸载:手动删除
说明:*.tar.gz/*.bz2形式的二进制软件包是用tar工具来打包、用gzip/bzip2压缩的,安装时直接解包即可。对于解压后只有单一目录的软件,卸载时用命令“rm -rf 软件目录名”;如果解压后文件分散在多处目录中,则必须一一手动删除(稍麻烦),想知道解压时向系统中安装了哪些文件,可以用命令“tar ztvf *.tar.gz”/“tar ytvf *.bz2”获取清单。tar的参数z是调用gzip解压,x是解包,v是校验,f是显示结果,y是调用bzip2解压,t是列出包的文件清单。更多的参数请参看手册页:man tar。
如果你更喜欢图形界面的操作,可以在X-Window下使用KDE的ArK压缩档案管理工具。
3、提供安装程序的软件包
这类软件包已经提供了安装脚本或二进制的安装向导程序(setup、install、install.sh等),只需运行它就可以完成软件的安装;而卸载时也相应地提供了反安装的脚本或程序。例如SUN公司的StarOffice办公软件套件就使用名为setup的安装程序,而且在软件安装后提供反安装的功能,目前这种类型的软件包还比较少,因其安装与卸载的方式与Windows软件一样,所以就无需多讲了。
二、源代码分发软件包的安装与卸载
Linux软件的源代码分发是指提供了该软件所有程序源代码的发布形式,需要用户自己编译成可执行的二进制代码并进行安装,其优点是配置灵活,可以随意去掉或保留某些功能/模块,适应多种硬件/操作系统平台及编译环境,缺点是难度较大,一般不适合初学者使用。
1、*.src.rpm形式的源代码软件包
安装:rpm -rebuild *.src.rpm
cd /usr/src/dist/RPMS
rpm -ivh *.rpm
卸载:rpm -e packgename
说明:rpm --rebuild *.src.rpm命令将源代码编译并在/usr/src/dist/RPMS下生成二进制的rpm包,然后再安装该二进制包即可。packgename如前所述。
2、*.tar.gz/*.tgz、*.bz2形式的源代码软件包
安装:tar zxvf *.tar.gz 或 tar yxvf *.bz2 先解压
然后进入解压后的目录:
./configure 配置
make 编译
make install 安装
卸载:make uninstall 或 手动删除
说明:建议解压后先阅读说明文件,可以了解安装有哪些需求,有必要时还需改动编译配置。有些软件包的源代码在编译安装后可以用make install命令来进行卸载,如果不提供此功能,则软件的卸载必须手动删除。由于软件可能将文件分散地安装在系统的多个目录中,往往很难把它删除干净,那你应该在编译前进行配置,指定软件将要安装到目标路径:./configure --prefix=目录名,这样可以使用“rm -rf 软件目录名”命令来进行干净彻底的卸载。与其它安装方式相比,需要用户自己编译安装是最难的,它适合于使用Linux已有一定经验的人,一般不推荐初学者使用。
关于Linux下软件的安装与卸载lanche已经讲了这么多,但可能还会有人问怎么知道一个tar.gz/bz2包是二进制文件包呢还是源代码包?如果你用过压缩工具就会明白,压缩包未必就是软件,它也可能是备份的许多图片,也可能是打包在一起的普通资料,要分辨它到底是什么最好的办法就是查看包里的文件清单,使用命令tar ztvf *.tar.gz / tar ytvf *.bz2或者在X-Window下使用图形化的ArK压缩档案管理工具都可以,源代码包里的文件往往会含有种种源代码文件,头文件*.h、c代码源文件*.c、C++代码源文件*.cc/*.cpp等;而二进制包里的文件则会有可执行文件(与软件同名的往往是主执行文件),标志是其所在路径含有名为bin的目录(仅有少数例外)。原来这么简单呀,还不快点自己试试!
跟踪堆内存块
可使用自定义的内存分配函数来进行内存块的跟踪,而这种函数最初被称为普通内存分配函数,举例来说,C语言程序中一般使用malloc(),尔后,自定义的内存分配会进行以下一系列的操作:
·在目前已分配的内存块列表中,跟踪新分配的内存块。
·决定是否向系统提交虚拟内存。
·如果虚拟内存已被提交,跟踪包含此内存块的堆范围,并更新上述堆内存块列表,以标识出从未被访问过的内存块。
还需要自定义的释放与重分配内存函数,以便通过程序中使用的内存块的地址与大小,来更新内存块列表。所跟踪的堆内存块列表应包含如下结构:
·内存块的基地址。
·自身大小
·用于指示自从上次虚拟内存被提交之后,内存块是否被访问过的布尔值。
当一个内存块被释放后,自定义的释放代码将会进行以下操作:
·如果自从上次堆扩展之后,内存块还未被访问过,将向程序报告。
·从列表中删除跟踪的内存块。
·判定系统是否已释放了包含此内存块的虚拟内存。
·如果虚拟内存被释放,要相应地更新,以反映剩余的堆范围。
当一个内存块被重新分配时,你的自定义重新分配内存代码必须进行以下两种操作:首先,在释放之后重新跟踪内存块,因为重新分配的内存块可能不在原位置;其次,在分配之后也要重新跟踪新的基地址及分配内存块的大小。另有一个可选的方法,你可检查是否重新分配的内存块被移动了,如果没有被移动,只需仅仅更新内存块跟踪列表,标出此内存块的大小;如果内存块还在同一基地址,但是增长了,此时就要检查堆扩展,并按照前述分配内存的方法重来一遍。
跟踪堆自身
堆跟踪取决于当内存块被分配或释放时,虚拟内存是否分别被提交或释放,依此可以建立一张堆内存范围跟踪表,以确定在程序运行期间,虚拟内存空间中堆的确切位置,跟踪列表中应包括如下数据:
·跟踪范围的基地址
·自身大小
在Windows操作系统中,这些值可通过HeapWalk()调用获得,此处要注意的是,HeapWalk()函数调用开销巨大,因此,只在程序需要时调用,而不是当有内存分配或释放时都调用。另一种Windows上的方法是使用IsBadReadPointer()函数,当一个内存块被释放后,你可以调用这个函数快速地判定包含此内存块的虚拟内存是否已被释放。另一个可以跨平台的备选方法是,可试着访问包含此内存块的虚拟内存,并捕捉可能发生的访问异常。此外,只有在一种情况下会考虑使用如HeapWalk()这样开销巨大的函数,就是需判定邻近剩余的已提交堆范围。
通过一种探测堆内存提交的算法,堆内存跟踪列表会不断地增长,如例1中所示。要注意的是,当你的程序分配一个内存块时,自定义的内存分配代码也能跟踪到这些内存块,并使用例1中的算法来更新包含内存块的堆列表。如果一个内存块的分配导致了额外的虚拟内存被提交,那么被内存块占用的虚拟内存会在之前就释放,或许之前就被用作别的用途。在任一情况中,必须有条件地更新堆范围跟踪列表: l 如果正在跟踪已提交范围的基地址,此时必须更新范围的大小,以指示出新的范围上限。
·否则,必须建立一张新的堆内存范围跟踪表。
如果一个新的内存块出现在一个之前未被跟踪的堆范围中,就满足了以上条件,此时明智地使用前述的系统调用可高效地跟踪堆内存范围。
当你的程序释放一个内存块时,自定义的内存释放代码会使用到如例2中的算法,此算法会先判定释放的内存是否与被跟踪的堆内存范围有关;接下来,必须检查已释放内存块占用的空间是否仍处于提交状态,如果是,表明了即使内存块被释放,虚拟内存的覆盖区也没有发生改变,否则,你的代码必须进行如例2结尾处的系统调用--如Windows中的HeapWalk()--以确保跟踪的堆是最新的,且包含堆内存块的虚拟内存已被释放。
如果虚拟内存已经被提交,那么你应该检查那些通常包含了最近被释放的堆内存块的内存范围,以确认是否有此范围内的内存被提交,而此范围内任何被提交的内存部分都应该是在一个堆内存范围内,特别是如果它包含了跟踪列表上的内存块,进行此检查可保证进一步的准确性。接下来还有以下两件事,如例2中所示:
·如果被跟踪的堆内存范围内任一部分被提交,必须更新你的跟踪列表。
·否则,删除列表中的元素并跟踪新的范围。
如果提交的部分在中间,那么就有可能把堆内存范围截成好几断,总而言之,在你放弃跟踪老的范围之前,应先在全范围内检查一下哪一部分仍处于提交状态。
程序中可能会用到好几个不同的堆,在Windows上,如果你调用HeapCreate()并把返回的句柄传给接下来的HeapAlloc()、HeapReAlloc()、HeapFree()函数调用时,就会创建一些不同的堆;另外,如果你加载了C运行时库DLL的多个实例,也会因为每个实例使用它们自己的堆而产生多个堆。此时,可在不同的列表中跟踪多个堆,也可在不同的列表中跟踪它们的内存块。首先,这样做的好处是,列表的查找可以变得很快,其次,当堆被"摧毁"时,你可以毫无顾虑地清除跟踪列表,但这需要在堆创建和释放时加入额外的代码来完成--即分别设置和删除对应的列表。另外,你也可管理一个堆内存块的列表及一个堆范围的列表,并在程序开始运行时建立它们。
一些专业的运行时分析工具也能对分配的内存块及堆范围进行跟踪,就像前面所说的自定义内存分配函数与释放函数一样,甚至还能通过基本的虚拟内存HASH值(ox187d690)进一步跟踪,以便为精确的运行时错误检测提供更加可靠的手段。但此处描述的方法并不足以帮助你理解何时才能找到通过程序可控制的堆内存块,减少虚拟内存消耗的时机。
找到适当的清理时机
为使用跟踪信息以精确定位那些导致虚拟内存不必要增长的堆内存块,你还必须要记录下内存访问动作,并在程序读写堆内存时,标记出相应的内存块跟踪结构。如果你的堆内存块都是通过存取函数访问的,那么很容易就可找到所有代码读写的内存部分,并以条件编译生成一个特定的版本。这些存取函数可查找你列表上被访问过的内存块,并设置布尔值作出标记。
当虚拟内存被提交生成一个堆,你的自定义内存分配函数应取消堆中所有内存块的标记,而在接下来,它们可能会重新被标记上,一个接一个,就像你的程序正在访问它们。当一个取消标记的内存块被释放时,相应的虚拟内存也会被释放,此时你自定义的释放函数就会先一步释放内存块,以减小虚拟内存的覆盖范围。
也可安排对堆内存块作一些临时的扫描,这也许可在每一次虚拟内存提交时进行。如果一个内存块在经过多遍扫描后仍保持未标记状态(也许会花很长时间),则包含此内存块的虚拟内存范围就必须对所跟踪的内存块数进行检查。如果那个内存块,或者一组被忽视的类似内存块,只是单独地与提交的虚拟内存有关系,那么通过释放与重新定位这些内存块,你可能已经找到一个减少程序虚拟内存要求的好方法。
如果你实现了此处描述的所有内存块和堆范围跟踪代码,而当这些所有的跟踪都起作用时,那么程序的速度将会变得很慢,主要是因为在每一次堆内存访问时,都会进行一遍列表查找,当然,也可以通过一些快速列表查找方法如二分法查找、跳跃查找之类的来缩短查找的时间,还可使用对应每个堆的单独列表来加速查找。如果程序使用了许多的堆内存块,并且也找到了减少额外虚拟内存消耗的方法,以往所花费的所有精力与耐心,与此时得到的回报相比,就算不上什么了。
[1] |