IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Linux  >

Linux on POWER 的 JNI 编程实例

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

Nikolay Yevik (yevik@us.ibm.com), Linux on POWER 技术顾问, IBM 

2005 年 3 月 14 日

本文通过一些简化的示例描述了重要的 Java 本地接口(Java Native Interface,JNI)编程概念,并在适当的地方着重指出了特定于 POWER™ 上 Linux™ 的以及通常的编程隐患。

简介

对那些刚接触 Java 本地接口编程,并且想要在用于 POWER 的 Linux 发行版本(比如 Red Hat Enterprise Linux 3 和 SUSE LINUX Enterprise Linux V9)上检验其 JNI 编程 能力的开发者来说,本文会有所帮助。





回页首


编译器

在本文中,示例中简化的源代码是在 SLES 9 和 RHEL AS 3 Update 3 上编译的,使用的是 64 位 IBM Java SDK 1.4.2 的最新服务更新(Service Refresh)以及用于 Linux 的 IBM XL C/C++ Advanced Edition V7.0。 注意,并不是要强制使用 IBM Java SDK 和 IBM XL C/c++ 编译器,只是这样做效果会非常好。 请随意使用其他编译器在用于 POWER 的 Linux 发行版本上编译 Java 和 JNI C/C++ 代码。

IBM XL C/C++ 编译器可以为 POWER™ 处理器生成性能极度优化的二进制代码,在大部分情况下, 这可以显著地提高所有 C/C++ 代码的性能。用于 Linux 的 IBM XL C/C++ 编译器与 GNU C/C++ 能够非常好地兼容,并且包含有 gxl* 编译器变量,可以将 GNU C/C++ 编译器标记翻译为相应的用于 IBM XL C/C++ 编译器的命令,然后调用它。当转而使用 IBM XL C/C++ 编译器时,这可以显著地 简化 Makefile 文件(为 GNU 所编写)的移植。





回页首


示例

本节所包含的示例基于通常使用的以及可公开获得的示例 JNI 代码,向您展示了如何在 POWER 上 Linux 中 编译这些示例。

通过 Java 代码示例调用本地函数



清单 1. HelloWorld.java

                    
class HelloWorld {
   /* Native method declaration */
   public native void displayHelloWorld();
     /* Use static intializer */
      static {
         System.loadLibrary("hello");
      }
      /* Main function calls native method*/
      public static void main(String[] args) {
        new HelloWorld().displayHelloWorld();
      }
}



清单 2. HelloWorld.c
                    
#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>
JNIEXPORT void JNICALL
Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj)
{
    printf("Hello world!\n");
    return;
}


编译
  1. 编译 Java 代码,生成 HelloWorld.class 文件:

    javac HelloWorld.java


  2. 使用 javah 工具生成 HelloWorld.h 文件:

     javah –jni HelloWorld
    



    清单 3. HelloWorld.h
                            
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class HelloWorld */
    #ifndef _Included_HelloWorld
    #define _Included_HelloWorld
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     HelloWorld
     * Method:    displayHelloWorld
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
      (JNIEnv *, jobject);
    #ifdef __cplusplus
    }
    #endif
    #endif
    


  3. 编译本地 C 代码:

    xlc_r –q64 –qarch=auto –qmkshrobj HelloWorld.c –o libhello.so
    



运行

java –cp . HelloWorld



输出

HelloWorld!



注意
  1. 所有本地 JNI 方法都应该在 Java 代码中声明。
  2. 应该在静态代码块中调用 System.loadLibrary()。
  3. 如果对代码的修改改变了函数的符号,那么需要重新运行 javah。
  4. 在本例中,我们创建了一个 64 位共享程序库,通过向线程安全可重入编译变量 xlc_r 传递一个 –q64 标记 来将它加载到 64 位 JVM 地址空间中。如果您使用的是 32 位 JVM,那么创建 32 位的共享 程序库。尝试将 64 位对象加载到 32 位地址空间,或者反之,都会导致 java.lang.UnsatisfiedLinkError
  5. 选项 –qarch=auto 生成的指令将在程序被编译的硬件平台上运行。IBM XL C/C++ 编译器有很多通用的和特定于硬件的编译器优化选项,让您能够生成极度优化的代码。
  6. 选项 –qmkshrobj 生成共享对象;gcc 中相应的选项为 –shared
  7. 要指向新创建的共享程序库的位置,可能需要设置 LD_LIBRARY_PATH 全局变量。

链接 JNI 共享对象示例

让我们来修改第一个示例,让 displayHelloWorld() 去调用来自 多个对象文件的函数。代码的 Java 部分,即 HelloWorld.java 文件,与前一个示例保持相同, 但我们修改本地代码。


清单 4. HelloWorld.c
                
#include <jni.h>
#include "HelloWorld.h"
#include stdio.h>
extern void func1(), func2(),func3();
JNIEXPORT void JNICALL
Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj)
{
    printf("Hello world!\n");
    func1();
    func2();
    func3();
    return;
}



清单 5. share1.c
                
/************
* share1.c: *
*************/
#include <stdio.h>
void func1()
{
   printf("func1 called\n");
}
void func2()
{
   printf("func2 called\n");
}



清单 6. share2.c
                
/************
* share2.c  *
*************/
void func3 ()
 {
   printf("func3 called\n");
 }



编译

下面的 Makefile 说明了编译的过程。输入 make 来开始编译。


清单 7. Makefile
                
# This definition makes JAVA_HOME refer to the JDK specified in your PATH.
# You can change this to refer to a specific directory
JAVA_HOME=/opt/IBMJava2-ppc64-142
libhello.so: shrsub.o HelloWorld.h
        xlc_r -M -I. -I$(JAVA_HOME)/include  -q64 -qarch=auto -
        qmkshrobj HelloWorld.c shrsub.o -o libhello.so
HelloWorld.h: HelloWorld.class
        $(JAVA_HOME)/bin/javah -jni HelloWorld
HelloWorld.class:
        $(JAVA_HOME)/bin/javac  HelloWorld.java
shrsub.o: c.o
        xlc_r -qmkshrobj -q64 -o shrsub.o share1.o share2.o
c.o:
        xlc_r -q64 -c share1.c
        xlc_r -q64 -c share2.c



运行

 java –cp . HelloWorld



输出

Hello world!
func1 called
func2 called
func3 called

使用静态编译时链接的 JNI Invocation API 用法示例

这个 C++ 示例展示了如何使用 JNI Invocation API —— 也就是说,将 JVM 嵌入到本地 C/C++ 代码之中, 然后调用以 Java 编写的应用程序中可执行的部分。


清单 8. invoke.cpp
                
#include <jni.h>
#include <stdlib.h>
#include <iostream.h>
int main() {
    JNIEnv *env;
    JavaVM *jvm;
    JavaVMInitArgs vm_args;
    JavaVMOption options[5];
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jobjectArray args;
     options[0].optionString = "-Xms4M";
     options[1].optionString = "-Xmx64M";
     options[2].optionString = "-Xss512K";
     options[3].optionString = "-Xoss400K";
     options[4].optionString = "-Djava.class.path=.";
     vm_args.version = JNI_VERSION_1_4;
     vm_args.options = options;
     vm_args.nOptions = 5;
     vm_args.ignoreUnrecognized = JNI_FALSE;
    /* Create the Java VM */
    res = JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args);
    if (res < 0) {
        cerr<< "Can't create Java VM" << endl;
        goto destroy;
    }
    cls = env->FindClass("Prog");
    if (cls == 0) {
        cerr << "Can't find Prog class" << endl;
        goto destroy;
    }
    mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
    if (mid == 0) {
        cerr<< "Can't find Prog.main" << endl;
        goto destroy;
    }
    jstr = env->NewStringUTF(" from C++!");
    if (jstr == 0) {
        cerr << "Out of memory" << endl;
        goto destroy;
    }
    args = env->NewObjectArray( 1,
                        env->FindClass("java/lang/String"), jstr);
    if (args == 0) {
        cerr << "Out of memory" << endl;
        goto destroy;
    }
    env->CallStaticVoidMethod( cls, mid, args);
destroy:
    if (env->ExceptionOccurred()) {
       env->ExceptionDescribe();
    }
    jvm->DestroyJavaVM();
}



清单 9. Prog.java
                
public class Prog {
        public static void main(String[] args) {
               System.out.println("Hello World" + args[0]);
        }
}



编译

下面的 Makefile 说明了编译的过程。输入 make 来运行它。


清单 10. Makefile
                
# This definition makes JAVA_HOME refer to the JDK specified in your PATH.
# You can change this to refer to a specific directory
JAVA_HOME=/opt/IBMJava2-ppc64-142
all:    invoke Prog.class
invoke:
        xlC -q64 -I$(JAVA_HOME)/include –
        L$(JAVA_HOME)/jre/bin/classic -L$(JAVA_HOME)/jre/bin -ljvm -o
         invoke invoke.cpp
Prog.class:
        $(JAVA_HOME)/bin/javac Prog.java
clean:
        rm -f *.class invoke *.o



运行

./invoke



输出

Hello World from C++!



注意
  1. 由于我们使用的是 64 位的 JVM,所以我们也必须将 C++ 调用代码编译为 64 位的。没有办法将 64 位 对象加载到 32 位地址空间,反之亦然。
  2. 我们将要使用的是 JNI_VERSION_1_4(0x00010004)。在最新的 JDK 中,调用 JNI_CreateJavaVM() 时传递给它的版本号再也不能是 JNI_VERSION_1_1(0x00010001)。 可以传递的版本号是 JNI_VERSION_1_2(0x00010002) 或 JNI_VERSION_1_4(0x00010004)。
  3. 当通过 JNI Invocation API 创建 JVM 时,通过向将要被调用的嵌入的 JVM 传递 JVM 参数,可以控制 JVM 参数,从而不 依赖于默认的值。例如,使用:
    -Xms<size> 来设置初始的 Java 堆大小;
    -Xmx<size> 来设置 Java 堆的最大值;
    -Xoss<size> 来设置任意线程的 Java 栈的最大大小;
    -Xss<size> 来设置任意线程的本地栈的最大大小。
    请注意,-X 选项不是标准的,可能会不加声明地被修改。
  4. 重要的是不要忘记同时从 ../jre/bin/classic 和 ../jre/bin 链接到 JVM 程序库。
  5. 对于具有使用 JNI Invocation API 的本地代码的所有者为 root 的文件,要记得设置 SETUID 位。 如果非 root 用户启动了这种所有者为 root 而且设置了 SETUID 的可执行文件,出于安全,LD_LIBRARY_PATH 会被清除,从而将找不到所需要的程序库。
  6. 可能需要设置 LD_LIBRARY_PATH 全局变量,令其指向 ../jre/bin/classic 和 ../jre/bin 目录中新创建的共享程序库和 JVM 程序库。

使用动态编译时链接的 JNI Invocation API 用法示例

此 C 代码示例展示了如何通过 dlopen()dlsym() 调用来使用程序库动态加载的 JNI Invocation API。Prog.java 文件的 Java 代码仍然与前一个示例相同。


清单 11. invoke.c
                
#include <jni.h>
#include <stdlib.h>
#include <dlfcn.h>
main() {
    JNIEnv *env;
    JavaVM *jvm;
    JavaVMInitArgs vm_args;
    JavaVMOption options[5];
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jobjectArray args;
    char classpath[1024];
    vm_args.version = 0x00010004;
    vm_args.options = options;
    vm_args.nOptions = 5;
    vm_args.ignoreUnrecognized = JNI_FALSE;
    options[0].optionString = "-Xms4M";
    options[1].optionString = "-Xmx64M";
    options[2].optionString = "-Xss512K";
    options[3].optionString = "-Xoss400K";
    options[4].optionString = "-Djava.class.path=.";
    void* lib_handle = 0;
    void* (*lib_func)() = 0;
    lib_handle = dlopen("/opt/IBMJava2-ppc64-
    142/jre/bin/classic/libjvm.so", RTLD_NOW);
    if (!lib_handle)
    {
       fprintf(stderr, "dlopen failed\n");
       goto destroy;
    }
    lib_func = (void*(*)())dlsym(lib_handle, 
    "JNI_GetDefaultJavaVMInitArgs");
    lib_func(&vm_args);
    lib_func = (void*(*)())dlsym(lib_handle, "JNI_CreateJavaVM");
    lib_func(&jvm,&env,&vm_args);
    cls = (*env)->FindClass(env, "Prog");
    if (cls == 0) {
        fprintf(stderr, "Can't find Prog class\n");
        goto destroy;
    }
    mid = (*env)->GetStaticMethodID(env, cls, "main", 
    "([Ljava/lang/String;)V");
    if (mid == 0) {
        fprintf(stderr, "Can't find Prog.main\n");
        goto destroy;
    }
    jstr = (*env)->NewStringUTF(env, " from C!");
    if (jstr == 0) {
        fprintf(stderr, "Out of memory\n");
        goto destroy;
    }
    args = (*env)->NewObjectArray(env, 1,
                        (*env)->FindClass(env, "java/lang/String"), jstr);
    if (args == 0) {
        fprintf(stderr, "Out of memory\n");
        goto destroy;
    }
    (*env)->CallStaticVoidMethod(env, cls, mid, args);
    destroy:
    if ((*env)->ExceptionOccurred(env)) {
         (*env)->ExceptionDescribe(env);
    }
    (*jvm)->DestroyJavaVM(jvm);
}



编译

下面的 Makefile 说明了编译的过程。输入 make 来运行它。


清单 12. Makefile
                
# This definition makes JAVA_HOME refer to the JDK specified in your PATH.
# You can change this to refer to a specific directory
JAVA_HOME=/opt/IBMJava2-ppc64-142
all:    invoke Prog.class
invoke:
        xlc -I$(JAVA_HOME)/include -L$(JAVA_HOME)/jre/bin/classic 
       -L$(JAVA_HOME)/jre/bin -q64 -qarch=auto -o invoke invoke.c -ljvm
Prog.class:
       $(JAVA_HOME)/bin/javac Prog.java
clean:
        rm -f *.class invoke *.o



运行

./invoke



输出

Hello World from C!



注意
  1. 在前一使用编译时链接示例中需要考虑的事项同样适用。
  2. 要使用 gcc 编译同一示例,则需要执行下面的命令,其中 JAVA_HOME 是您的 JDK 安装主目录:

    gcc -I$(JAVA_HOME)/include  
    -L$(JAVA_HOME)/jre/bin/classic -L$(JAVA_HOME)/jre/bin 
    -m64 -rdynamic -o invoke invoke.c -ldl –ljvm



参考资料



关于作者

Nikolay Yevik 是 IBM eServer Solutions Enablement 团队的一名 Linux 技术顾问, 他有 5 年多在 UNIX 平台上进行 C、C++ 和 Java 开发的经验。 他拥有石油工程和计算机科学硕士学位。您可以通过 yevik@us.ibm.com 与他联系。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?







回页首


© IBM Corporation 1994-2005. All rights reserved. References in this document to IBM products or services do not imply that IBM intends to make them available in every country. Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. Linux is a trademark of Linus Torvalds in the United States, other countries, or both.

Information is provided "AS IS" without warranty of any kind.

All customer examples described are presented as illustrations of how those customers have used IBM products and the results they may have achieved. Actual environmental costs and performance characteristics may vary by customer.

Information concerning non-IBM products was obtained from a supplier of these products, published announcement material, or other publicly available sources and does not constitute an endorsement of such products by IBM. Sources for non-IBM list prices and performance numbers are taken from publicly available information, including vendor announcements and vendor worldwide homepages. IBM has not tested these products and cannot confirm the accuracy of performance, capability, or any other claims related to non-IBM products. Questions on the capability of non-IBM products should be addressed to the supplier of those products.

All statements regarding IBM future direction and intent are subject to change or withdrawal without notice, and represent goals and objectives only. Contact your local IBM office or IBM authorized reseller for the full text of the specific Statement of Direction.

Some information addresses anticipated future capabilities. Such information is not intended as a definitive statement of a commitment to specific levels of performance, function or delivery schedules with respect to any future products. Such commitments are only made in IBM product announcements. The information is presented here to communicate IBM's current investment and development activities as a good faith effort to help with our customers' future planning.

Performance is based on measurements and projections using standard IBM benchmarks in a controlled environment. The actual throughput or performance that any user will experience will vary depending upon considerations such as the amount of multiprogramming in the user's job stream, the I/O configuration, the storage configuration, and the workload processed. Therefore, no assurance can be given that an individual user will achieve throughput or performance improvements equivalent to the ratios stated here.

Photographs shown are of engineering prototypes. Changes may be incorporated in production models.

IBM grants you a nonexclusive copyright license to use all programming code examples from which you can generate similar function tailored to your own specific needs. All sample code is provided by IBM for illustrative purposes only. These examples have not been thoroughly tested under all conditions. IBM, therefore, cannot guarantee or imply reliability, serviceability, or function of these programs. All programs contained herein are provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed

其他公司、产品或服务的名称可能是其他公司的商标或服务标志。

IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款