原帖地址:http://www.linuxdiyf.com/blog/?291/action_viewspace_itemid_405.html
实现Linux应用二进制兼容的意义
随着操作系统技术的发展从注重性能到注重服务,操作系统所配套的服务软件多少以及开发环境是否易用就成为决定一个操作系统生命力的关键因素。微软今日垄断地位的形成,就得益于微软完整的应用软件体系。而以前国产操作系统未能形成良性发展的局面,也是受阻于没有完整的软件体系的配套。所以,注重配套服务软件开发,提高系统适用面就成为Kylin操作系统在设计和开发只初就统筹考虑的问题。
通过自己的力量,从头为Kylin操作系统开发来所有应用软件时间不允许,人力、物力上不可行。它山之石可以攻玉,我们必须借助目前现有成熟的应用软件体系才能在较短时间内构成较完整的应用软件体系,满足用户的基本需求。目前的应用软件体系主要有Windows体系、UNIX体系和Linux体系等。Windows应用软件体系具有应用数量、种类丰富,使用界面友好等特点,但存在操作系统大量技术细节不公开,难以支持的问题。UNIX体系同样应用数量巨大,但存在版本众多,并且各UNIX版本间不完全兼容的问题,大大限制了各UNIX版本间应用的移植,并且UNIX应用大多数无法免费获得,也极大的限制了UNIX应用的普及。Linux应用软件体系目前已形成一个从操作系统到数据库管理系统,从中间件软件到丰富的应用软件的完整的软件体系,正成为标准的软件基础设施。由于依托开源软件体系,所以Linux操作系统的技术细节全部公开,并且遵循LSB应用接口。同时,许多Linux应用体系的软件也采用了开源或免费方式,从而也极大地推动了Linux操作系统的成功。Kylin操作系统采用了兼容Linux二进制应用的技术路线,支持同平台的Linux二进制应用不加任何修改地运行在Kylin操作系统上。通过兼容Linux应用体系,Kylin操作系统可以站在巨人的肩膀上,极大地改善市场生存环境,快速构成完整的Kylin体系,从而加快Kylin操作系统的应用推广。
应用二进制兼容的主流实现技术
应用二进制兼容技术根据兼容实现层次可以分为硬件层兼容、操作系统层兼容、系统库层兼容三种实现方式。
硬件层兼容技术以虚拟机技术为代表,如VMWare和Virtual PC等。虚拟机技术通常构建在操作系统(一般称为HOS)之上,通过仿真硬件虚拟出完整的硬件系统。并在虚拟的硬件系统上安装要兼容的操作系统(一般称为GOS),并运行相应的应用程序。虚拟机环境中的被兼容操作系统由于运行在用户态,无法直接操纵物理硬件,而必须通过虚拟机监视器(VMM)来中转对硬件的特权操作,如页表访问、I/O访问等。通过虚拟机技术的应用兼容方式对应用透明性好,兼容性好,但存在实现难度较大,性能低下等缺陷:据评测,应用虚拟一般降低约30%,尤其是IO性能,损失更大。
操作系统层兼容通过在操作系统内核中支持新的兼容操作系统系统调用来实现不同应用体系间的兼容。运行兼容应用时,操作系统将调用兼容操作系统的系统调用入口函数,完成对兼容应用的支持。采用操作系统层兼容技术同样对兼容应用透明,兼容应用运行性能高。但存在兼容面窄的局限性,如UNIX与Linux体系间的兼容较容易实现,但UNIX、Linux与Windows体系间的兼容较困难。目前IBM的K42操作系统、GNU/BSD操作系统以及SUN的JANUS计划均采用该技术方案实现了UNIX体系对Linux体系的兼容。
通过构造专门的动态连接库,在用户库一级实现对兼容应用的支持是目前Wine等系统所采用的兼容技术途径。用户库级的兼容技术途径具有简单和快捷的特点,但仿真层在动态链接库中实现,不适用于静态链接好的程序。
Kylin操作系统中Linux应用二进制兼容的设计
通过分析和比较 基于硬件仿真实现兼容的虚拟机技术、基于系统调用仿真实现的内核层兼容技术、以及修改系统共享库实现的核外兼容技术等多种兼容技术,并同时考虑到效率和兼容效果、以及Kylin操作系统的内在特点,我们确定了以系统调用仿真实现的内核层兼容技术作为基础实现Linux的应用兼容。
由于Linux操作系统的发行版本众多,Linux系统的标准化是目前Linux世界的大势所趋,其中由自由软件组织发起的旨在规范Linux标准的LSB计划获得了众多Linux厂商的响应和支持,红帽、曼德拉等许多厂商均获得了LSB的认证。用LSB规范作为Kylin对Linux应用兼容的标尺,使得Kylin作为Linux应用平台同国际标准接轨成为我们的追求目标。
操作系统二进制兼容的总体结构
Kylin操作系统通过在内核中支持符合LSB标准的Linux系统调用实现了对Linux应用的兼容,并同时提供了对Linux应用加载、proc文件系统等的支持模块。Linux应用兼容模块的具体结构如下图所示:
二进制兼容的实现
在确定上述技术路线后,通过解决下述的几个关键技术点,我们在Kylin操作系统内核中的系统服务层实现了Linux二进制兼容服务。
Kylin对Linux应用二进制兼容的总体结构
Kylin操作系统的Linux应用加载模块
操作系统为不同格式的文件创建初始执行环境时完成的工作大部分是相同的;主要区别在于需要根据文件的类型或格式为其建立用户空间和用户态堆栈。Kylin操作系统提供了对多种文件格式的加载器,完成对不同种类文件的识别和加载过程(这里的加载主要指对用户空间的建立,不包括对用户态堆栈的创建)。Kylin将系统中所有的加载器组织在一起成为一个加载器队列。对于所有的文件,在其开头的128个字节里都包含了关于该文件属性的必要且充分的信息。因此,内核首先读入执行文件的头128字节,然后让加载器队列中的加载器轮流分析该文件头,认领可执行文件。如果某个加载器辨认到了它代理的可执行文件,加载该文件的任务就交给它;否则内核就让队列中的下一个加载器继续进行尝试。对于不同操作系统的ELF文件,其加载过程是相同的。因此,Kylin只实现一个ELF文件加载器,其完成对所有ELF文件的识别和加载工作。当然,该加载器在识别出ELF文件后,仍需进一步对该文件来自的操作系统进行识别,至此才算完成对文件格式的确立工作。相同格式但来自不同操作系统的应用程序,对用户态堆栈的初始布局也可能存在不同的要求。对于这样的异地应用程序,需要对Kylin为本地文件创建的用户态堆栈进行修改,以满足其执行要求。Kylin使用堆栈修补过程来完成这项工作。虽然系统为所有ELF文件建立用户空间所做的工作都相同,但动态连接的Kylin ELF文件和Linux ELF文件,对用户态堆栈的初始布局却存在不同的要求。为此,Kylin为Linux ELF文件设计了堆栈修补过程。
Kylin操作系统的Linux系统调用仿真模块
系统调用集合定义了内核向用户进程提供的服务接口,用户应用可通过该接口使用操作系统提供的有关设备管理、输入/输出系统、文件系统、进程控制、通信以及存储管理等方面的功能,而无需了解操作系统的内部结构和有关的硬件细节,从而减轻用户负担、保护系统、提高资源利用率。在大多数操作系统上,应用程序发出系统调用,到系统调用返回的整个过程主要包括下面几个步骤:
·应用程序通过API请求服务;
·库函数中的封装例程通过某种与硬件平台相关的自陷指令发出系统调用;
·自陷指令将执行模式变为内核态,执行系统调用处理程序,其取得系统调用的参数并调用对应的系统调用服务例程,在系统调用服务例程执行完毕后,对由系统调用服务例程得到的返回值进行必要的处理,然后返回用户态;
·库函数中的封装例程将由操作系统内核得到的返回值交给应用程序。
Linux应用程序在Kylin上运行时,被连接到原始的Linux共享库。因此,Kylin对Linux系统调用进行仿真,首先需要对本地的系统调用处理程序进行必要的修改,使其处理来自异地的系统调用请求时,能够:
·取得由Linux系统调用封装例程提供的参数;
·按Linux的约定将系统调用返回值传递给用户态的异地系统调用封装例程;
·使用Linux系统调用接口表中的系统调用服务例程完成来自Linux应用程序的服务请求;
Kylin有一个系统调用接口表,其中每个表项对应一个系统调用服务例程。表项由两部分组成:系统调用参数的个数和指向系统调用服务例程的函数指针。系统调用接口表中表项的排列顺序与系统调用号的顺序对应。因此,可以将系统调用号转换为对系统调用接口表的索引,并执行对应表项中函数指针所指向的系统调用服务例程,完成应用程序请求的服务。由于Kylin的系统调用与Linux的系统调用,从名称、系统调用号、参数接口形式到具体的功能,都存在一定的差异,因此,对Linux应用程序中发出的系统调用请求,不能将其连接到Kylin本地的系统调用接口表,或直接使用本地的系统调用服务例程为其服务,而是需要设计一个Linux系统调用接口表,并对其中的系统调用在Kylin上进行模拟实现。
我们分两步实现对Linux系统调用的仿真。首先,实现Linux系统调用接口表;然后,在Kylin上对接口表Linux系统调用接口表中的系统调用服务例程进行仿真。
从与硬件平台是否相关的角度对Linux的系统调用进行划分,可将其分成硬件平台相关的系统调用和硬件平台无关的系统调用两类。对于与硬件平台无关的系统调用,只需在Kylin上仿真一次;对于与硬件平台相关的系统调用,则需要针对不同的硬件平台(I386或IA64),分别在Kylin上进行仿真。对于所有的Linux系统调用,包括硬件相关和无关的、或者完成不同功能的,在Kylin上对其进行仿真的思路和方法都相同。首先,分析Linux系统调用的参数形式、返回值、实现的功能等情况;然后考察Kylin对该系统调用的支持情况;在完成上述准备工作后,就可以开始实现对该系统调用服务例程的仿真。对异地系统调用服务例程的仿真有三种情况:
第一,直接使用本地的系统调用服务例程完成所需的功能。
某些Linux系统调用,在Kylin上正好存在与之完全对应的系统调用,即两者在参数个数、参数类型、完成的功能、返回值等方面都完全吻合。对于这些系统调用,可以直接使用本地的系统调用服务例程完成其所需的功能。通过向Linux系统调用接口表中这些系统调用对应的表项直接写入指向本地系统调用服务例程的函数指针,可以实现这个目标。
第二,通过某种形式的转换后,调用本地系统调用服务例程完成所需的功能
存在一些Linux系统调用,在Kylin上有与之功能相同或相似的系统调用,但双方在使用的数据结构、标志的定义等方面还存在一定差异。Kylin为这些系统调用设计了一个封装函数,封装函数通过调用本地的系统调用服务例程完成Linux系统调用的主体工作,但在调用本地函数的前后,封装函数对Linux和Kylin之间存在的各种差异进行转换。在Linux系统调用接口表中这些系统调用对应的表项里,写入指向对应的封装函数的函数指针。另外,对于所有不是由本地系统调用服务例程直接实现的Linux系统调用服务例程,Kylin在其名字前加上前缀linux_,以和本地系统调用服务例程相区别;对于这些系统调用的参数所使用的数据类型或数据结构,如果与本地对应的数据类型或结构不相同,或者在本地根本不存在对应物,则按照Linux的约定,在本地重新对其进行定义,在其名字前也加上前缀linux_。
第三,对异地系统调用服务例程在本地进行重新实现。
存在一些Linux系统调用,在Kylin上不存在功能与之相似的系统调用。对于这些系统调用,只能在Kylin上对其进行重新实现。
Kylin操作系统的Linux proc文件系统仿真模块
Proc文件系统是Linux系统内核对外接口之一,它是一个存在于内存中的伪文件系统, Linux应用程序通过该文件系统获得内核信息或修改内核参数。目前,Kylin操作系统中实现了Linux proc文件系统仿真框架,它主要提供系统进程信息、系统设备信息等必要的内核信息,供Linux应用程序使用。
Kylin中的proc文件系统框架的由信息采集和信息输出两个子系统构成。其中信息采集子系统负责收集内核信息;信息输出子系统部分负责提供对外接口,根据需要调用信息采集例程,并将内核信息封装成Linux-proc的格式向外输出。
由于proc文件系统需要的内核信息覆盖虚存管理系统、文件系统、网络协议、设备驱动等各个方面,因此信息采集子系统由分布在基本内核层和系统服务层各个子系统中的若干信息收集例程构成。
信息输出子系统作为系统服务层中的一个服务模块,以文件系统的形式向外提供信息。Kylin操作系统中实现了一个伪文件系统框架,该文件系统框架支持伪文件系统的创建、维护等操作。在该文件系统框架基础上,出于安全性的考虑,我们实现了一个Linux的proc文件系统的子集,提供有限内核信息的读操作接口。
Kylin操作系统的LSB标准化
为了实现Linux应用兼容的标准化,我们研究了LSB规范,LSB中对操作系统平台作了详细的规范,最基础要求包括:系统调用兼容性;系统库兼容性;系统工具兼容性;文件层次结构兼容性。LSB通过提供测试集来实现对标准的实施。目前lsb1.3标准中使用LSB-VSX、LSB-FHS、LSB-OS、LSB-USRGROUPS四个主要的测试族; 用于对上面几个标准进行符合性测试。
由于在实现过程中,我们使用LTP提供的系统调用测试集作为Linux二进制系统调用的测试试程序,对系统调用兼容进行了全面而细致的调测试,确保了操作系统调用的兼容性。
由于Kylin操作系统内核层实现了Linux系统调用兼容,内核中对应用加载方式和应用符号解析进行了标准化支持,确保了函数库的接口可以在运行时与应用连接,支持Linux标准的ELF格式,并按LSB标准提供了动态连接机制;因此Linux的系统库无需修改就可以直接调用到Linux的系统调用。目前Kylin系统中配置了支持LSB标准的c库、数学库等常用系统库。
由于二进制兼容在内核中的实现,Kylin操作系统可以支持绝大多数Linux应用;Kylin操作系统平台可支持Linux标准RPM的应用程序安装方式。这样系统工具兼容只需要根据标准安装LSB中规定的系统工具应用程序即可实现。如用户/组管理工具等。
LSB规范中对文件系统组织结构有专门的规范,称为文件层次规范(FHS)。采用该文件目录组织方式,用户在使用、生成、配置操作系统系统时与Linux完全相同。Kylin操作系统的Linux文件目录组织结构主要涉及以下两类文件:
Linux二进制应用:由于Kylin操作系统直接支持Linux二进制应用,故只要在路径设置时符合Linux的缺省路径设置,用户就可以按与Linux环境相同的方式使用Linux二进制应用,无任何区别。
动态链接库:Kylin操作系统通过可执行文件加载器中的动态链接库路径选项定义了Linux二进制应用的存放位置。为了支持Linux的文件目录组织结构,Kylin操作系统的可执行文件加载器定义了缺省的加载路径为“/”,加载器采用标准的ld_linux.so加载器。
Kylin操作系统上Linux二进制兼容的实现现状和未来工作
目前, Kylin在I386体系结构的平台上可以支持oracle数据库、apache服务器等大型Linux应用程序;并且已经通过I386体系结构的系统上的LSB1.3标准测试;同时在IA64体系机构上实现了Linux兼容的基本功能,可以运行小规模的Linux程序。
Kylin上的Linux二进制兼容工作已经取得了较好的结果,我们将继续针对I386体系结构在性能上作进一步的优化,并完善IA64上对Linux二进制兼容工作。