近期项目需要使用Java调用本地一个很老的C写的库,就顺便研究了下Java调用本地代码的一些方法。整理一下出一个小系列。主要就是介绍两种不同的方法:JNIJNA。作为第一篇,我们先动手用C写个简单的动态链接库,为后面的文章做准备。

什么是动态链接库

先做个基础的普及。C/C++编译出来的库有两种:静态链接库和动态链接库。

静态库后缀名在Windows上是.lib,Unix/Linux上是.a。当你的程序在编译时引用静态库,编译器会将整个静态库都包含在你编译后的可执行文件中,所以可执行文件会很大,但是程序执行时就不再需要静态库了。

动态库后缀名在Windows上是.dll,一般存放在C:\Windows\System32下;Unix/Linux上是.so,一般存放在/lib/usr/lib下。程序编译时,编译器不会将动态库包含在生成的可执行文件中,所以引用动态库的可执行文件较小,程序会在运行过程中动态加载所需要的库。对于Java程序员就可以将它简单的想象成.jar文件。在Unix/Linux上,动态链接库一般都命名为”libxxx.so”,其中”xxx”是库名。

创建动态链接库

这里我们使用Linux环境,用C语言创建一个动态链接库”libhello.so”,库里面就提供一个hello()函数。根据输入的字符串,在屏幕上打印相应的欢迎信息。

  • 编写头文件”hello.h”,声明hello()函数
#ifndef _HELLO_H_
#define _HELLO_H_

/* C++需加上extern "C" 声明 */
#ifdef __cplusplus
extern "C" {
#endif

void hello(const char *);

#ifdef __cplusplus
}
#endif
#endif
  • 编写程序文件”hello.c”,根据输入参数打印Hello信息
#include "hello.h"
#include <stdio.h>

void hello(const char *name)
{
    printf("Hello %s!\n", name);
}
  • 编译生成动态链接库

    $ gcc hello.c -fPIC -shared -o libhello.so
    

现在你就可以在当前目录下找到”libhello.so”文件。这样,动态链接库就创建好了。我们先写个C程序测试下。

测试动态链接库

我们写个C程序来调用刚才创建的动态链接库。

  • 编写测试程序”test.c”
#include "hello.h"

void main(int argc, char *argv[])
{
    if (argc > 1)
    {
        hello(argv[1]);
    }
    else
    {
        hello("World");
    }
}
  • 编译并生成可执行文件

    $ gcc test.c -L . -lhello -o test
    

    参数”-L .“表明除了系统指定的目录外,还会从当前目录里搜索需要引用的库。参数”-lhello”指明需要链接名为hello的库,也就是文件名为”libhello.so”的库。编译后,会在本地生成名为”test”的可执行文件。

  • 查看可执行文件的依赖库
    Linux提供”ldd”命令来查看可执行文件运行时需依赖的动态链接库。我们查看下刚才生成的”test”文件:

    $ ldd ./test
    

    命令执行后,你会看到类似于下面的这段信息

        linux-vdso.so.1 =>  (0x00007fffba3fe000)
        libhello.so => ./libhello.so (0x00007f20a996b000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f20a958e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f20a9b6f000)
    

    从第二行中你可以看到,该程序依赖于库”libhello.so”,而且该库文件存在于当前目录下./libhello.so。如果你将该文件移走,这里就会显示”not found”,程序自然也无法执行。

  • 运行可执行文件
    在运行前,你先要将当前目录加到环境变量LD_LIBRARY_PATH中。程序执行时,会在其中搜索需加载的库。

    $ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
    

    运行测试程序

    $ ./test Billy
    

    你可以看到屏幕上打印出了

    Hello Billy!
    

    恭喜你,测试通过!

用C++也一样,这里就不再做描述了。下一篇,我们将介绍如何使用JNI的方法从Java语言调用刚才创建的动态链接库。

本例代码可以从这里下载