(转)研究 Port Layer

什么是 Port Layer

长久以来,程序的跨平台能力一直是程序员努力的方向:从最早期的微程序,到 POSIX 标准;从 COBRA 到 COM/COM+,再到 Java “Write once, run everywhere” 思想的提出。然而,在使用不具有跨平台能力的语言,比如 C/C++,要开发一套在多种平台上都能运行的程序依然是很困难的。这是因为,不同平台上的系统调用都是不同的,如果要实现程序的跨平台,似乎开发者必须为不同的平台各自开发一套程序。可是,现代的 OS 有 Windows,Linux,AIX,HP-UX,Solaris,SCO Unix,Novell NetWare,AS/400,MVS;计算机体系结构有 IA32,IA64,Power RISC, Spark,Itanium,Alpha 等等;想象一下,不同的操作系统和计算机体系结构能产生多少种平台组合?如果其中每一种平台都需要相应地实现一套程序,对于开发者来说简直就是一场噩梦。所以,要实现这些想法,就往往要借助于 Port Layer。

Port Layer 是位于操作系统 API 和应用程序之间的函数库。在 Port Layer 内部,不同平台拥有 Port Layer 的不同实现,在这些实现中使用了实际平台的系统调用。而对外,Port Layer 提供了一套统一的 API,屏蔽了底层系统的差异,从而使得开发者既不必关心软件究竟在何种平台上开发,也不用关心在何种平台上运行。在当今的软件世界里,许多需要在不同平台上开发、运行的软件,比如 HTTP 服务器,数据库等等,都可能需要 Port Layer。比如 Apache Http Server,就使用了 APR(Apache Portable Runtime),使它在不同平台上都可以运行。

作为开源 Java SE(Java Standard Edition) 的实现—— Harmony 来说,跨平台能力是最基本的要求:Java 是迄今最好的跨平台语言之一。在 Harmony 的实际开发过程中,我们不可避免地要使用一系列系统调用:内存操作,读写文件,网络交互等等,这些都依赖于操作系统的支持,而这些系统调用都是平台相关的,如果要为不同的平台开发不同的底层代码,非常费时费力。其次,Harmony 的总体构架是模块化的,不仅不同的类库包彼此独立,VM 和类库也彼此独立,这就要求有良好的底层支持。这些至关重要的需求,迫切需要引入一个 Port Layer。

Harmony 使用了 Harmony Port Lib 作为自己的 Port Layer。Harmony Port Lib 首先来自 IBM J9 开发团队的捐献。J9 虚拟机现在是 IBM JDK 5 的缺省虚拟机,它是从 J2ME 实现衍生出来的。我们知道,J2ME 上的平台差异性非常大,对 Port Layer 的要求比较高,而该 Port lib 很好地满足了这个需求。而在 Java SE 的 Harmony 实现上,稍做改动,这个 Port Lib 也非常适用。


回页首

Harmony 项目中的 Port Layer

Apache Harmony Port Layer 定义在操作系统与虚拟机之间,也称为 Harmony Port Lib,它是由一个标准 C 的库(Port Library)来实现的。Port Library 与操作系统交互,为虚拟机和类库的本地代码提供了一个平台无关的标准 C 语言 API 访问系统调用。诸如文件 I/O,网络 I/O, 内存操作,信号处理,以及错误处理等等功能,都被纳入 Port Library 的范围。

Figure 1. Apache Harmony – 体系结构
Apache Harmony – 体系结构

通过使用 Port Library,所有(虽然目前还没有做到,但这是其设计目标)与操作系统平台相关的内容被封装在一个库里。在 Port Library 的基础上,开发者可以开发出平台无关的内存、线程管理、JNI、和为类库服务的 native 代码。这样,由于 Port Library 的存在,将 Java SE 的庞大类库和虚拟机代码移植到其他操作系统的工作就可以被快速的开发出来,从而大大提高了可移植性和程序的可维护性。


回页首

Harmony Port Layer 的结构

Harmony Port Library 很好地屏蔽了底层调用的差别,为上层提供了高效的 API。

数据类型

Harmony Port Library 首先定义了一系列的通用变量类型:

通用变量类型

				
 UDATA; /* unsigned 64bits, same as U_64 */ 
 U_64; 
 U_32; 
 U_16; 
 U_8; 
 IDATA;  /* signed 64bits, same as I_64 */ 
 I_64; 
 I_32; 
 I_16; 
 I_8; 
 BOOLEAN; 

其中,U 和 I 分别代表无符号数和有符号数;64/32/16/8 分别表示数据长度。头文件 hycomp.h 将这些通用变量类型映射到具体的平台数据类型。这里,我们以 64 位有符号数 I_64 和无符号数 U_64 为例:

在 win32 平台上的数据类型

				
 typedef __int64 I_64; 
 typedef unsigned __int64 U_64; 

在 Linux32 平台上的数据类型

				
 typedef long long I_64; 
 typedef unsigned long long U_64; 

在 Linux64 平台上的数据类型

				
 typedef long int I_64; 
 typedef unsigned long int U_64; 

而对于 Port Library 的用户来说,只存在 I_64 和 U_64 这类跨平台的通用变量,从而在数据层面上使编写可移植的 Java 类库成为可能。

实例访问

与其它 Port Layer 不同,Harmony Port Library 的良好设计,它不仅仅可以看作是一个函数库,更可以看作一个以 C 实现的一个类。Harmony Port Library 可以生成不同的实例,有各自独立的数据变量,还有各自独立的虚函数表。所谓 Harmony Port Library 的实例,就是数据结构 struct HyPortLibrary。

Figure 2. Port Library 核心数据结构 struct HyPortLibrary
Port Library 核心数据结构 struct HyPortLibrary

每次产生一个这样的数据结构,就可以说,生成了一个 Harmony Port Library 的实例。事实上,为了避免 Harmony Port Library 中的数据相互干扰,在 Harmony Java Virtual Machine 每次初始化时都会生成一个 Harmony Port Library 的实例。

接下来,我们具体分析一下 struct HyPortLibrary。

struct HyPortLibrary

				
 typedef struct HyPortLibrary { 
	 /** portVersion*/ 
	 struct HyPortLibraryVersion portVersion; 
	 /** portGlobals*/ 
	 struct HyPortLibraryGlobalData *portGlobals; 
	 /** a serials of port library functions*/ 
	 I_32 (PVMCALL port_shutdown_library) (struct HyPortLibrary * portLibrary); 
	 ... 
	 UDATA (PVMCALL shmem_stat) (struct HyPortLibrary * portLibrary, 
	 const char *name,  
	 struct HyPortShmemStatistic * statbuf); 
 } 

struct HyPortLibraryVersion 记录了当前 Port Library 的版本信息。struct HyPortLibraryGlobalData 存储了一系列与当前 Port Library 相关的全局变量。其中包括一些控制变量,互斥锁 / 信号量,缓冲区,错误代码以及线程本地存储(Thread Local Storage)。事实上,Port Library 所有的状态信息都存储在这个结构之中。只要定义不同的 struct HyPortLibrary,就可以在同一台机器,甚至同一个进程内使用不同的 Port Library,而这些 Port Library 的状态不会互相干扰。 比如:

两个 HyPortLibrary 的使用

				
 //define two port libraries 
 HyPortLibrary hyPortLibrary1; 
 HyPortLibrary hyPortLibrary2; 
 //get their pointers 
 HyPortLibrary * portLibrary1 = &hyPortLibrary1; 
 HyPortLibrary * portLibrary2 = &hyPortLibrary1; 
 //initialize the current version of the port library 
 HyPortLibraryVersion portLibraryVersion;   
 HYPORT_SET_VERSION(&portLibraryVersion, HYPORT_CAPABILITY_MASK); 
 //initialize the port library 
 hyport_init_library(portLibrary1, &portLibraryVersion, sizeof(HyPortLibrary)); 
 hyport_init_library(portLibrary2, &portLibraryVersion, sizeof(HyPortLibrary)); 

上面这段代码生成了不同的两个 Port Library: hyPortLibrary1 和 hyPortLibrary2。它们各自拥有独立的全局变量。至于 Harmony Port Library 的销毁,可以使用这样的 API:

HyPortLibrary 的销毁

				
        hyport_shutdown_library(&portLibrary1); 
     

这样关于 portLibrary1 的所有信息就被销毁了。struct HyPortLibrary 的余下部分是一系列函数指针。hyport_init_library 初始化了这些函数指针,使它们指向 Port Library 实现的对应函数。我们说,这是一个虚函数表,是因为不仅这个函数表是 Harmony Port Library 使用者的唯一入口,而且 Harmony Port Library 内部函数之间的所有调用也都是通过这个函数表进行的。这是与大多数其他 Port Layer 实现,内部函数调用一般为静态链接,大不相同的地方。

这样的设计分离了 Port Library 的接口与实现,用户可以将合适的函数替换掉 Port Library 的实现。事实上,这样的替换可以在 Port Library 初始化之后动态替换!

下面将以 win32 平台为例,替换 Port Library 中 file_seek 函数。首先定义一个 file_seek 的实现。

HyPortLibrary 中函数的取代 - 实现 file_seek

				
 //file_seek defined by user 
 I_64 my_file_seek (struct HyPortLibrary * portLibrary, IDATA fd, 
                   I_64 offset, I_32 whence){ 
	 //gets the low 32-bit of offset 
	 LONG distance_low = (LONG)offset; 
	 //gets the pointer to the high 32-bit of offset 
	 PLONG distance_high = (PLONG)((BYTE *)&offset + 4); 
	 //uses the win32 API to set the file pointer 
	 return SetFilePointer((HANDLE)fd,distance_low,distance_high,whence); 
 } 

然后替换 Port Library 中相应的实现。

HyPortLibrary 中函数的取代 - 替换

				
 portLibrary1-> file_seek = my_file_seek。

这样,使用 portLibrary1 中的 file_seek,调用的就是我们设计的 my_file_seek 了。

HyPortLibrary 中函数的取代 - 使用

				
 IDATA fd = portLibrary->file_open(portLibrary1,"SomeFile",0,0); 
 portLibrary1->file_seek(portLibrary1,fd,HyOpenRead,0); 

回页首

函数功能

Port Library 提供了丰富的函数调用。

内存

包含了内存分配管理函数。

HyPortLibrary 中的函数 - 内存分配管理函数

				
 // 用来分配内存空间的函数
 void * hymem_allocate_memory ( 
	 struct HyPortLibrary *portLibrary, 
	 UDATA byteAmount); 
 // 还有回收已分配空间的:
 void hymem_free_memory ( 
	 struct HyPortLibrary *portLibrary, 
	 void *memoryPointer)  
 // 以及重新分配内存大小的: 
 void * hymem_reallocate_memory ( 
	 struct HyPortLibrary *portLibrary, 
	 void *memoryPointer, 
	 UDATA byteAmount)     
 

提供了虚拟内存管理函数:

HyPortLibrary 中的函数 - 虚拟内存管理的内存分配函数

				
 // 用来分配内存空间的函数
 hyvmem_reserve_memory ( 
	 struct HyPortLibrary *portLibrary, 
	 void *address, 
	 UDATA byteAmount, 
	 struct HyPortVmemIdentifier *identifier, 
	 UDATA mode, UDATA pageSize);    

与 不同,虚拟内存管理的内存分配函数并不实际分配一块物理内存,而只是在进程的虚拟内存表中预定一块空间,不允许其他操作使用,但不实际分配内存。直到调用下面的函数才分配内存。

HyPortLibrary 中的函数 - 虚拟内存管理的内存分配函数

				
 // 用来分配内存空间的函数
 hyvmem_commit_memory ( 
	 struct HyPortLibrary *portLibrary, 
	 void *address, 
	 UDATA byteAmount, 
	 struct HyPortVmemIdentifier *identifier);    

虚拟内存函数特别适合用于缓冲区的管理:先预定一块足够大的虚拟内存空间;当缓冲元素被加入缓冲区时,再真正分配物理内存,存储该元素。一方面,预定虚拟内存空间并不代表物理内存的使用,可以节约资源;另一方面,避免了缓冲区增长时出现内存碎片。

包含一系列将文件内容映射到进程空间的函数,如

HyPortLibrary 中的函数 - 文件内容映射到进程空间的函数

				
 void *  hymmap_map_file ( 
	 struct HyPortLibrary *portLibrary, 
	 const char *path, 
	 void **handle);    

当然,并不是所有的操作系统都支持内存映射功能,但是 Harmony Port Library 仍旧提供这样的 API,只不过这些平台上的 Port Layer 函数是通过将文件内容读取到指定内存地址处实现的。不过,对于 Harmony Port Library 的用户应用程序来说,这一切都是透明的。这里,我们再次看到了 Port Layer 在跨平台编程中的重要作用。

线程

包含了管理线程生命周期的功能。如 hythread_create 产生一个线程,hythread_self 返回当前线程,hythread_suspend 挂起当前线程,hythread_resume 继续挂起的线程,hythread_sleep 使线程休眠一段时间,hythread_interrupt 中断指定线程,以及 hythread_cancel 结束特定线程等等。此外, 还提供了线程本地存储功能 : hythread_tls_alloc,hythread_tls_set 以及 hythread_tls_free 等等。

同步

提供了关于信号量的一系列函数。用户可以用 hyshsem_open 产生一个信号量,可以用 hyshsem_wait 等待事件的发生,还可以用 hyshsem_post 唤醒等待中的线程。

值得一提的是, 中 Harmony Port Library 还提供了平台无关的信号量处理函数。其主要作用是,接受操作系统的信号量,安全地关闭 Java 虚拟机,并记录下虚拟机的状态信息,以便于调试。--> 其中重要的函数有两个:

   
    void VMCALL hygp_register_handler( 
		 struct HyPortLibrary *  portLibrary,    
		 handler_fn  fn, void *  aUserData);    
 

该函数注册了一个回调函数 fn , 当 Java Virtual Machine 非正常中止的时候,fn 负责记录并发送 VM 的状态信息给用户应用程序。实际上, 作为 Port Layer 与 Java Virtual Machine 之间的接口,hygp_register_handler 是部分依赖于虚拟机实现的。特别是回调函数,它将负责虚拟机安全关闭以及状态记录,一般是由虚拟机实现者提供的。目前 Harmony Project 使用的回调函数是 中定义的 vmGPHandler。

另一个函数是:

 UDATA VMCALL hygp_protect ( 
	 struct HyPortLibrary *  portLibrary,  
	 protected_fn  gn, 
	 void * arg);    
 

该函数产生一个操作系统线程,当操作系统向这个线程发送信号的时候,gn 将决定是否关闭 VM。若是,gn 将负责调用 hygp_register_handler 注册的回调函数,以返回 VM 的状态信息。不幸的是,目前的 Harmony Port Library 提供的 hygp_protect 函数并不像其名称所表示的那样,是“protected”的。事实上,它不保证线程收到信号时,会使用 hygp_register_handler 注册的回调函数安全关闭虚拟机。

文件

提供了大多数文件系统的 API。如读 / 写文件的 hyfile_read 和 hyfile_write;移动文件指针的 hyfile_seek;查找文件的 hyfile_findfirst 和 hyfile_findnext;移动文件的 hyfile_move 以及删除文件的 hyfile_unlink 等等。

网络

< 提供了通用 SOCKET 编程所需要的功能。包括:产生 socket,绑定本地地址 (bind),连接 (connect),关闭 (close),查询 socket 当前状态,设定 socket 状态,等等。

错误处理

Harmony Port Library 则使用了类似 POSIX 的错误返回方式。首先,定义了一系列错误码,分别表示函数调用中出现了何种错误。Harmony Port Library 的函数返回值仅提示函数调用成功完成与否。但不幸的是,有时 0 代表调用失败;有时 -1 代表调用失败。程序开发人员请仔细阅读每个 Harmony Port Library API 的文档,以确定调用的错误返回值。至于真正的错误码,还记得吗?曾经提到过,Harmony Port Library 最重要的核心数据结构 struct HyPortLibrary 包含线程本地存储 (TLS)。是的,错误码就被记录在这个线程本地存储之中了。

线程,本地存储,是不是还有同步?听上去很复杂吗?没关系,提供了方便用户使用的函数调用,直接获得错误码。

 I_32 error_last_error_number(struct HyPortLibrary *portLibrary); 

如果用户想要获得错误码,请在发生错误之后立即将它取走:

HyPortLibrary 中的函数 - 取错误的函数。

				
 //allocate a block of memory 
 void * address = portLibrary-> hymem_allocate_memory (portLibrary,  100); 
 //failed 
 if(address ==0) 
 { 
	 //get error code 
	 I_32 errno = portLibrary-> error_last_error_number(portLibrary); 
	 swtich(errno) 
	 //deal with error 
	… .. 
 }    

错误码存在于线程本地存储,它在整个线程内是具有全局意义的变量。准确地说,所谓错误码,就像 error_last_error_number 函数名所提示的那样:是该线程发生的最后一个失败的函数调用的错误码。如果没有立即把当前的错误码取走,它会被下一个发生的失败的函数调用的错误码所覆盖。

Harmony Port Library 也提供了设置错误码的函数:

HyPortLibrary 中的函数 - 设置错误码的函数。

				
 I_32 error_set_last_error ( 
	 struct HyPortLibrary * portLibrary, 
	 I_32 platformCode, 
	 I_32 portableCode);   
 

其中,portableCode 就是 Port Library 中通用的错误码。至于 platformCode,那是平台相关的错误码,一般指具体平台系统返回的错误码,如 win32 的 GetLastError() 的值。有了 Port Library 中通用的错误码,为什么还需要 platformCode ?这里的主要原因是不同平台上的错误码太多了,portableCode 不可能如此细分以表达所有平台上的所有错误。在调试过程中,platformCode 对于确定问题所在是有重要帮助的。当然,Port Library 的基本作用就是跨平台的,所以使用 Port Library 的开发程序中一般不希望出现依赖于 platformCode 的代码, 以避免引入平台相关性。

除了错误代码,Harmony Port Library 提供了更人性化的 error message 来表示错误:

HyPortLibrary 中的函数 - 错误信息相关

				
 I_32 hyerror_set_last_error_with_message ( 
	 struct HyPortLibrary *portLibrary, 
	 I_32 portableCode, 
	 const char *errorMessage); 
 const char *VMCALL  hyerror_last_error_message (struct HyPortLibrary *portLibrary); 
 

还有一点,当用户设计自己的 Port Library 函数时,请记住,在函数内部每次发生错误返回之前,务必用 error_set_last_error 设置错误代码。否则,error_last_error_number 得到的将是这次调用之前的错误码,这是会误导用户的。

版本信息

和所有的软件一样,Harmony Port Library 也有其自身的生命周期,也在不断发展和完善之中,也会出现不同的版本。而在这些不同版本之间,也会有兼容性问题。和大多数软件用诸如“2.6.4”这样的数字来标示版本号不同,Harmony Port Library 除了通常的主版本号 / 次版本号之外,直接用其支持的功能来表示版本信息。目前 Harmony Port Library 支持的功能分为基本功能,文件系统,Socket 和页面四个方面,分别用四个常量 HYPORT_CAPABILITY_STANDARD,HYPORT_CAPABILITY_FILESYSTEM,HYPORT_CAPABILITY_SOCKETS 和 HYPORT_CAPABILITY_LARGE_PAGE_SUPPORT 表示。

HyPortLibrary 的版本常量

				
 #define HYPORT_CAPABILITY_STANDARD  1 
 #define HYPORT_CAPABILITY_FILESYSTEM  2 
 #define HYPORT_CAPABILITY_SOCKETS  4 
 #define HYPORT_CAPABILITY_LARGE_PAGE_SUPPORT  8 

而 Harmony Port Library 的版本就是这四方面功能的总和 : 定义了一个 64 位无符号数 HYPORT_CAPABILITY_MASK。它是上述四个常量按位或的结果。

HyPortLibrary 的版本信息

				
 #define HYPORT_CAPABILITY_MASK ( 
	 (U_64)(HYPORT_CAPABILITY_STANDARD | 
	 HYPORT_CAPABILITY_FILESYSTEM | 
	 HYPORT_CAPABILITY_SOCKETS | 
	 HYPORT_CAPABILITY_LARGE_PAGE_SUPPORT)); 
 

Harmony Port Library 的应用程序,可以通过 HYPORT_CAPABILITY_MASK 了解所需要的功能是否为目前的 Port Library 所支持。显然,目前的 HYPORT_CAPABILITY_MASK 是 15。当然,随着不断发展,新功能的加入,Harmony Port Library 提供的功能将不仅局限于当前这四个方面,HYPORT_CAPABILITY_MASK 也会不断进化。


回页首

与其他 Port Layer 的比较

Harmony Port Layer 与其他的 Port Layer 比较,有其自己的特点。下面重点与 Apache Portable Runtime(APR) 做一比较。

在 Apache 软基金会资助的开源项目中,有一个类似的平台无关库叫做 Apache Portable Runtime(APR), 这个项目是从 Apache Http Server 等项目的开发过程中提炼和分离出来的,目前已经比较成熟,它与 Harmony 的 Port Layer 的目标有类似之处,但是也有明显的不同,从而使得目前 APR 还不适合作为 Java 虚拟机和类库的平台无关库实现。

1 . Port Layer 结构

APR 是典型的函数库。例如,在 win32 平台上,它是一个 DLL 文件,函数和全局变量是通过符号定义处的 RVA(相对虚拟地址)直接导出给用户的。而 Harmony Port Library 不同,它提供了类似虚函数表的导出方式,能够在不重新编译整个 Java 虚拟机的前提下,替换 Port Library 中的任意一个函数。例如,用户有一个高效管理内存的函数实现,只要改变虚函数表中的函数指针,就可以轻易地取代 Harmony Port Library 中的对应函数,而对 Harmony Project 的其他部分不会产生任何影响。

2 .内存分配

Apache Portable Runtime 提供了三级空间分配体系。

Figure 2. Apache Portable Runtime 三级空间分配体系
Apache Portable Runtime 三级空间分配体系

不仅如此,由于 Apache Portable Runtime 从 Apache Http Server 等项目的开发过程中提炼和分离出来的,它更倾向于为服务器程序服务,每次分配内存的都有一定的粒度。请看下面的程序:

Apache Portable Runtime

				
 #include "headers/apr_allocator.h"
 #include "headers/apr_general.h"
 #include 
 
 
  
   
 int main() 
 { 
	 apr_initialize(); 
	 apr_allocator_t* p_allocator; 
	 apr_allocator_create(&p_allocator); 
	 apr_memnode_t *  p_apr_memnode = apr_allocator_alloc (p_allocator, 10); 
	 int length = (p_apr_memnode->endp)-(p_apr_memnode->first_avail); 
	 printf("The size of the first block allocated is %d/n",length); 
	 apr_allocator_destroy (p_allocator); 
	 apr_terminate(); 
	 return 0; 
 } 
 
 

运行该程序,得到输出:

The size of the first block allocated is 8168.

没错,尽管用户在 apr_allocator_alloc 中仅仅要求分配 10 个字节的空间,但事实上,通过计算 memnode 的尾地址 endp 与首个可用地址 first_avail,可以看到 memnode 居然有将近8K。这样的做法,在 http server 的应用上有较大价值。有兴趣的话,读者可以试一下,APR 第二次内存分配的粒度。

而 Harmony Port Library 的内存分配函数是直接调用平台相关的内存分配 API 的。

Harmony Port Library 似乎更适合作为 Java VM 和 Java 类库的基础。Java 世界的多样性,从 VM 内部的系统栈,用户栈,用户堆,缓冲区到 Java 类库中 ByteBuffer 容量变化以及 Java 应用程序中用户缓冲区,决定了统一的内存分配政策很可能是行不通的,至少在底层,在内存管理需求不甚明确的 Port Lay 层是行不通的。Harmony Port Library 只是实现平台无关性的工具;实际上,Port Library 自身不提供任何内存管理。Java VM 内部的系统栈,用户栈,用户堆,缓冲区的分配与回收是由 Java VM 管理的;Java 类库中的空间管理,诸如 ByteBuffer 的容量增长,是由 Java 类库实现的;Java 应用程序的空间管理,诸如用户缓冲区的设计是由 Java 应用程序实现的。

3 .为 Java 类库开发提供的额外功能

Harmony Port Library 分别提供了基本操作,线程操作以及内存 / 缓冲区操作的一系列函数,这些基本的功能是 APR 也具有的。除此之外,Harmony Port Library 根据 Java 类库的需求,为了提高执行效率,以及方便类库开发,定制了一些高级功能。


回页首

怎样在开发中使用 Port Library

由于 Harmony Port Library 的良好设计,在代码开发过程中使用 Port Library 调用其 API 函数是相当简单的。

在开发过程中,首先要注意的是,开发平台无关的代码,所有的变量都必须定义为平台无关的类型。Harmony Port Library 定义了大量平台无关的数据类型,比如基本类型符号整数(I_32),无符号整数(U_64,U_32),网络相关的数据结构 socket(hysocket_structure),address(hysock_addr),依靠这些平台无关的数据类型,开发者就不用考虑数据的实际平台特性了。

在上一部分中,我们知道对 Port Library 的 API 调用可以先生成一个 HyPortLibrary,然后使用其函数指针来调用,比如我们要申请一段长度为 1024 的内存:

Harmony Port Library 的使用 - 内存申请

				
 // 首先初始化 port library 
 hyport_init_library( 
	 portLibrary1, 
	 &portLibraryVersion, 
	 sizeof(HyPortLibrary)); 
 // 调用 API 
 void* mem = portLibrary1-> hymem_allocate_memory(portLibrary1,1024); 

不仅如此,Harmony Port Library 提供了一套宏来简化调用,它们是:

Harmony Port Library 的使用 - 宏定义

				
 PORT_ACCESS_FROM_ENV(JNIEnv) //if you have a JNIEnv struct ptr 
 PORT_ACCESS_FROM_JavaVM(JavaVM) // if you have a JavaVM struct ptr 
 PORT_ACCESS_FROM_PORT(portlib)  // if you have a portlib ptr 
 

这套宏可以让 Harmony 的开发者分别从 JNIEnv,VM 或者直接从某个 portlib struct 变量中取得指针,然后就可以使用一些简化过的宏代替函数指针来调用 API。比如在 Harmony 的 native code 的开发中,由于使用了 jni 调用,我们拥有一个 JNIEnv 指针 env,那么使用

 PORT_ACCESS_FROM_ENV(env); 

之后,我们就可以用:

   
 void* mem = hymem_allocate_memory(1024)
 

来代替上述的

   
 void* mem = portLibrary1-> hymem_allocate_memory(portLibrary1,1024); 
 

从而简化了调用。一般用户还可以通过初始化一个 Portlib,使用 PORT_ACCESS_FROM_PORT(portlib) 来简化调用。

在 Harmony 的开发过程中,大量地使用了 native code 来调用系统底层,port Library 就为这种调用提供了便利。下面我们就用一个简单的例子来说明。

接口定义

要从 Java 层调用到底层 c code,首先在 Java code 中定义一个接口,使用 native 关键字:

Harmony Port Library 的使用 - 定义接口

				
 public native long doSomething(int size); 

这个函数的实现将在底层 native code 中实现,在 Java 层,我们可以直接调用该函数,就好像调用一个普通的 Java 函数那样。

生成头文件

编译包含该函数定义的 Java 文件之后,就可以使用 Javah 来生成一个 .h 文件,里面包含了该函数的原型。比如,我们在 SocketChannelImpl.java 中定义了上述 native 函数接口,我们使用以下命令:

Harmony Port Library 的使用 - 生成头文件

				
 Javah org.apache.harmony.nio.internal.SocketChannelImpl 

就会生成一个 org_apache_harmony_nio_internal_SocketChannelImpl.h,包含了所有我们定义的函数原型。随后就是利用 port Library 实现该函数。

Harmony Port Library 的使用 - 生成的头文件

				
#include 
 
 
  
   
/* Header for class org_apache_harmony_nio_internal_SocketChannelImpl */    
#ifndef _org_apache_harmony_nio_internal_SocketChannelImpl 
    #define _org_apache_harmony_nio_internal_SocketChannelImpl 
    #ifdef __cplusplus 
    extern "C" { 
    #endif 
    /* 
    * Class:     org_apache_harmony_luni_platform_OSMemory 
    * Method:    isLittleEndianImpl 
    * Signature: ()Z 
    */ 
    JNIEXPORT jboolean JNICALL 
        org_apache_harmony_nio_internal_SocketChannelImpl_doSomething( 
        JNIEnv *, jclass, jobject, jint); 
   
    #ifdef __cplusplus 
    } 
    #endif 
#endif
 
 

数据定义

在数据定义中,要使用 port library 或者 jni 所定义的平台无关的数据结构,比如:

Harmony Port Library 的使用 - 定义数据结构

				
 ... 
 hytimeval_struct timeP;// port library 定义的时间结构体
 I_32 result = 	 0; 	 // 平台无关的 32 位整数
 jobject gotFD; 	 // Java 对象指针
 hysocket_t hysocketP;  // port library 定义的 Socket 结构体指针
 ... 

启动 Port Library

由上所述,启动 Port Library 比较简单。

Harmony Port Library 的使用 - 定义数据结构

				
 hyport_init_library(&hyportLibrary, &portLibraryVersion, sizeof(HyPortLibrary)); 
 if (0 != rc) { 
	 // handle error condition 
 } 

然后使用函数指针来调用 API

Harmony Port Library 的使用 - 使用函数指针来调用 API

				
 portLibrary->mem_allocate_memory(portLibrary, 1024); 

当然,在 Harmony 里,Port Library 随着 VM 启动而得到初始化,因此在 Harmony 中,我们可以使用一个更简单的方法:

使用宏定义

使用宏定义可以简化我们对 port library 的函数调用。在函数定义我们可以看到,调用该函数可以得到一个 JNIEnv 的指针,那么我们使用:

Harmony Port Library 的使用 - 初始化宏

				
 PORT_ACCESS_FROM_ENV (env); 

来得到简化的宏定义。

函数体实现

接下来就是对函数实际逻辑的实现。port library 提供的丰富的 API 接口,可以实现 Java API 所需的所有功能。比如文件的读写,网络操作等等。我们不必直接调用底层,而调用 port library 的函数。比如读文件,我们可以使用 hyfile_read 来代替不同平台上不同的读文件操作,避免了为一个操作写几份几乎相同的代码。当然在使用这些 API 的时候,不要忘记 include 必要的头文件。

错误处理

Port Library 同样提供了一整套错误处理机制。在 hyporterror.h 中,定义了平台无关的错误代码,很容易就判断当前的错误信息。首先,我们可以在执行某条语句之后,立即判断是否出错,如果出错,除了从某些函数的返回值可以得到错误代码之外,还可以用:

Harmony Port Library 的使用 - 取得错误代码

				
 I_32 errno = portLibrary-> error_last_error_number(portLibrary); 

取得它的错误代码。 Port Library 也有设置错误代码记录和错误信息的机制,他们是:

Harmony Port Library 的使用 - 设置错误代码

				
 I_32 VMCALL hyerror_set_last_error ( 
	 struct HyPortLibrary *portLibrary, 
	 I_32 platformCode, 
	 I_32 portableCode)  // 设置错误代码
 I_32 VMCALL hyerror_set_last_error_with_message ( 
	 struct HyPortLibrary *portLibrary, 
	 I_32 portableCode, 
	 const char *errorMessage).  // 设置错误信息    
 I_32 errno = portLibrary-> error_last_error_number(portLibrary); 
 

可以记录当前的错误代码,以及他们的的错误信息。

资源释放

完成所有的任务之后,我们应该释放所有申请的资源,同时,在结束时使用:

Harmony Port Library 的使用 - 释放 Port Library

				
 I_32 VMCALL hyport_shutdown_library (struct HyPortLibrary * portLibrary); 

来释放 Port Library。

编译运行

在完成代码编写之后,我们可以把这些代码编译后与 port library 链接成 .DLL(.so)文件,并在 Java 层用 System.loadLibrary(filename) 的方式载入,这样就可以调用该 native method 了。而在 Harmony 的开发过程中,VM 会自己装载动态链接库,可以直接调用。


回页首

结束语

Harmony Port Layer 是 Harmony 项目的重要部分,它为上层的 Java 虚拟机和类库的本地代码提供了一个平台无关的标准 C 语言 API 访问系统调用。

通过使用 Harmony Port Library,Harmony 项目才能够开发出平台无关的内存、线程管理,JNI,和为类库服务的 native 代码,并且大大提高了可移植性和程序的可维护性。

在未来的开发过程中,Harmony Port Library 在增加新的功能,更好的为 class lib 服务之外,也将支持更多的平台,如 OS X,和 64 位操作系统等等。

参考资料

学习

获得产品和技术

  • Apache Harmony 的二进制版本 下载
  • IBM Apache Harmony 开发包(VM Environment) 下载
  • IBM JDK 下载

讨论

作者简介

吕晶对计算机科学,开源软件,和新鲜事物都抱有浓厚的兴趣。

李夷磊 ,目前就职于 IBM 中国开发中心 Harmony 开发团队,主要负责类库开发的技术工作,对 Java,OS 以及 system programming 有浓厚的兴趣。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值