目前在工作中我遇到了JNI下C共享库中的全局变量问题。目标是将数据库源代码编译到共享库中,以便在Java应用程序中本机使用它。
在编译共享库之前,我使用它的常规Makefile编译源代码(这个过程太详细了,不能在这里显示)。然后我按如下方式编译共享库:
gcc -shared -fPIC -DPIC -g -o ../java-lite/src/main/resources/libmonetdb5.so `find common gdk mal/mal mal/modules mal/optimizer sql embedded -name "*.o" | tr "\n" " "` -lpthread -lpcre -lz -lm -ldl
然后我做了Maven编译:
mvn clean install
在Java方面,我只是在应用程序的引导程序中加载共享库。
static {
try {
// Try load the library from java.library.path (it's working)
System.loadLibrary("monetdb5");
} catch (UnsatisfiedLinkError e) {
//Load the library from the jar otherwise
loadLibFromJar("monetdb5");
}
}
问题出现在数据库加载上。这是JNI电话:
JNIEXPORT jobject JNICALL Java_nl_cwi_monetdb_embedded_MonetDBEmbeddedInstance_StartDatabase(JNIEnv *env, jclass class, jstring dbDirectory, jboolean silentFlag, jboolean sequentialFlag) {
const char *dbdir_string_tmp = (*env)->GetStringUTFChars(env, dbDirectory, 0);
char silent_char = 0, sequential_char = 0, *err = NULL, *databaseDirectory = strdup(dbdir_string_tmp);
jclass resultClass;
jmethodID resultConstructor;
(void)class;
if (silentFlag) {
silent_char = 1;
}
if (sequentialFlag) {
sequential_char = 1;
}
err = monetdb_startup(databaseDirectory, 1, 0);
if (err != NULL) {
(*env)->ReleaseStringUTFChars(env, dbDirectory, dbdir_string_tmp);
resultClass = (*env)->FindClass(env, "java/io/IOException");
(*env)->ThrowNew(env, resultClass, err);
return NULL;
}
resultClass = (*env)->FindClass(env, "nl/cwi/monetdb/embedded/MonetDBEmbeddedInstance");
resultConstructor = (*env)->GetMethodID(env, resultClass, "<init>", "(Ljava/lang/String;ZZZ)V");
return (*env)->NewObject(env, resultClass, resultConstructor, dbDirectory, silentFlag, sequentialFlag, 1);
}
然后从数据库源代码中使用monetdb_startup函数:
static int monetdb_embedded_initialized = 0;
void* monetdb_connect(void) {
Client conn = NULL;
if (!monetdb_embedded_initialized) {
return NULL;
}
conn = MCforkClient(&mal_clients[0]);
if (!MCvalid((Client) conn)) {
return NULL;
}
if (SQLinitClient(conn) != MAL_SUCCEED) {
return NULL;
}
((backend *) conn->sqlcontext)->mvc->session->auto_commit = 1;
return conn;
}
char* monetdb_startup(char* dbdir, char silent, char sequential) {
opt *set = NULL;
volatile int setlen = 0;
str retval = MAL_SUCCEED;
char* sqres = NULL;
void* res = NULL;
void* c;
if (setlocale(LC_CTYPE, "") == NULL) {
retval = GDKstrdup("setlocale() failed");
goto cleanup;
}
GDKfataljumpenable = 1;
if(setjmp(GDKfataljump) != 0) {
retval = GDKfatalmsg;
// we will get here if GDKfatal was called.
if (retval == NULL) {
retval = GDKstrdup("GDKfatal() with unspecified error?");
}
goto cleanup;
}
if (monetdb_embedded_initialized) goto cleanup;
if (!mal_init_inline) {
mz_ulong decompress_len_mal = EMBEDDED_SCRIPT_SIZE_MAX;
mz_ulong decompress_len_sql = EMBEDDED_SCRIPT_SIZE_MAX;
mal_init_inline = GDKzalloc(decompress_len_mal);
createdb_inline = GDKzalloc(decompress_len_sql);
if (!mal_init_inline || !createdb_inline) {
retval = GDKstrdup("Memory allocation failed");
goto cleanup;
}
if (mz_uncompress(mal_init_inline, &decompress_len_mal, mal_init_inline_arr, sizeof(mal_init_inline_arr)) != 0 ||
mz_uncompress(createdb_inline, &decompress_len_sql, createdb_inline_arr, sizeof(createdb_inline_arr)) != 0) {
retval = GDKstrdup("Script decompression failed");
goto cleanup;
}
}
setlen = mo_builtin_settings(&set);
setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_dbpath", dbdir);
BBPaddfarm(dbdir, (1 << PERSISTENT) | (1 << TRANSIENT));
if (GDKinit(set, setlen) == 0) {
retval = GDKstrdup("GDKinit() failed");
goto cleanup;
}
GDKsetenv("monet_mod_path", "");
GDKsetenv("mapi_disable", "true");
if (sequential) {
GDKsetenv("sql_optimizer", "sequential_pipe");
}
if (silent) THRdata[0] = stream_blackhole_create();
if (mal_init() != 0) { // mal_init() does not return meaningful codes on failure
retval = GDKstrdup("mal_init() failed");
goto cleanup;
}
if (silent) mal_clients[0].fdout = THRdata[0];
monetdb_embedded_initialized = 1;
c = monetdb_connect();
if (c == NULL) {
monetdb_embedded_initialized = 0;
retval = GDKstrdup("Failed to initialize client");
goto cleanup;
}
GDKfataljumpenable = 0;
// we do not want to jump after this point, since we cannot do so between threads
// sanity check, run a SQL query
sqres = monetdb_query(c, "SELECT * FROM tables;", 1, &res);
if (sqres != NULL) {
monetdb_embedded_initialized = 0;
retval = sqres;
goto cleanup;
}
monetdb_cleanup_result(c, res);
monetdb_disconnect(c);
cleanup:
mo_free_options(set, setlen);
return retval;
}
我们有一个全局变量&#34; monetdb_embedded_initialized&#34;表示数据库是否已初始化。在&#34; monetdb_startup&#34;函数,我们将变量设置为1然后我们在&#34; monetdb_connect&#34;上创建一个简短的连接。函数使用基本SQL查询进行小型功能测试。我们必须检查&#34; monetdb_embedded_initialized&#34;设置为1或不设置(对于后连接)。
问题是将monetdb_embedded_initialized设置为&#34; monetdb_startup&#34;,on&#34; monetdb_connect&#34;变量读为0,就像从未改变过一样。我测试了其他全局变量,实际上这种行为也会发生。
我在GDB上检查过只有一个线程调用这些函数,这个变量也只用在这些函数上。使用
在GDB上奇怪p monetdb_embedded_initialized
在每次读取之前显示正确的值。我还用&#34; nm libmonetdb5.so&#34;检查了符号,所有这些都在那里。
我知道全局变量一般都存在问题,但我更愿意保留它们,因为数据库源代码有很多,删除它们会花费我们太长时间。
感谢您的关注