该函数首先执行一些 memset 操作,然后调用基本上创建/获取 JVM 的函数,然后与 JVM 进行通信。
int
acCreateHeader(acInputParms *acCreate, acReturnParms *acRet3){
int Debug;
int rc;
acInputParms acInp;
kc0InputParms kc0Inp;
kc0ReturnParms kc0Ret3;
initACReturnParms(acRet3);
rc = invokeKc000(kc0Inp, &kc0Ret3); // JNI Operations
return rc;
}
initACReturnParms(acRet3);
的扩展如下:
void initACReturnParms (acReturnParms *in) {
memset(in->msgId,0,sizeof(in->msgId));
memset(in->msgText,0,sizeof(in->msgText));
memset(in->returnHdrTrl,0,sizeof(in->returnHdrTrl));
memset(in->returnPaySrc,0,sizeof(in->returnPaySrc));
memset(in->returnPayLoc,0,sizeof(in->returnPayLoc));
return;
}
因此,当我执行整个程序时,第一个 JNI 操作发生得很好,但程序的逻辑是以这样的方式构建的:函数 acCreateHeader
被调用两次,因此该程序在第二次调用中卡住在函数 (**jvm)->GetEnv(*jvm, (void**)&env, JNI_VERSION_1_4);
(它基本上尝试获取已创建的 JVM)。
抱歉,由于程序非常大,几乎不可能将我的所有代码粘贴到此处。
但问题就在这里:
当我通过注释 initACReturnParms
的调用复制函数 initACReturnParms
的所有逻辑并将其粘贴到函数 acCreateHeader
时(如下所示) ,即使在第二次 JNI 操作之后,程序也根本不会崩溃,并且整个程序按我的预期工作。
int
acCreateHeader(acInputParms *acCreate, acReturnParms *acRet3){
int Debug;
int rc;
acInputParms acInp;
kc0InputParms kc0Inp;
kc0ReturnParms kc0Ret3;
memset(acRet3->msgId,0,sizeof(acRet3->msgId));
memset(acRet3->msgText,0,sizeof(acRet3->msgText));
memset(acRet3->returnHdrTrl,0,sizeof(acRet3->returnHdrTrl));
memset(acRet3->returnPaySrc,0,sizeof(acRet3->returnPaySrc));
memset(acRet3->returnPayLoc,0,sizeof(acRet3->returnPayLoc));
//initACReturnParms(acRet3);
rc = invokeKc000(kc0Inp, &kc0Ret3); // JNI Operations
return rc;
}
那么,为什么这个函数 initACReturnParms
会在这里调用罪魁祸首呢?请帮助我理解。
如果您希望我提供更多信息/代码,请告诉我。
更新:
acRet3
是函数 acCreateHeader
中的局部变量,并作为参数从以下函数传递:
void writeLSHeader() {
int rc;
fillHdrTrl();
rc=acCreateHeader(&acCreate,&acRet);
//rest of the codes
}
并且 acRet
在下面所示的函数中初始化:
int fillHdrTrl() {
//Codes here
memset(&acRet,0,sizeof(acReturnParms));
memset(&acRet.returnHdrTrl[0],' ',AC_HDR_TRL_SZ);
//Codes here
}
acRet
是一个全局变量,内存是在堆栈上分配的:
acReturnParms acRet;
并且 acReturnParms
看起来像这样:
struct acReturnParms {
char msgId[9];
char msgText[61];
char returnHdrTrl[351];
char returnPaySrc[9];
char returnPayLoc[3];
}
上面使用的宏出现在头文件中,如下所示:
#define AC_HDR_TRL_SZ 350
更新2
以下是 JNI 操作的代码,这可能是导致这种未定义行为的原因:
#include "jni_funct.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int invokeKc000(kc0InputParms kc0inp, kc0ReturnParms *kc0ret)
{
JavaVM *jvm;
JNIEnv *env;
kc0InputParms input;
kc0ReturnParms *kc000out;
char *kc910Path, *kc910Lib;
int retcode;
retcode = 8;
kc910Path = getenv("KC910_BIN");
kc910Lib = getenv("KC910_LIB");
if(kc910Path == NULL || strlen(kc910Path) <= 0){
return -1;
}
if(kc910Lib == NULL || strlen(kc910Lib) <= 0){
return -1;
}
env = createKc000Vm(&jvm, kc910Path, kc910Lib);
if(env == NULL){
printf("\n JNIEnv is NULL");
return -2;
}
input = kc0inp;
kc000out = kc0ret;
communicateKc000(env, input, &kc000out, &retcode);
return retcode;
}
JNIEnv* createKc000Vm(JavaVM **jvm, char *kc910Path, char *kc910lib)
{
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options;
args.version = JNI_VERSION_1_4;
args.nOptions = 1;
char *classPath = "shared/misc/kc000";
char *javaClassPathOption = "-Djava.class.path";
char javaOptionString[2000];
char kc910Lib[1000], kc910Bin[1000];
memset(kc910Lib, '\0', sizeof(kc910Lib));
memset(kc910Bin, '\0', sizeof(kc910Bin));
memset(javaOptionString, '\0', sizeof(javaOptionString));
strncpy(javaOptionString, javaClassPathOption, strlen(javaClassPathOption));
strncpy(kc910Lib, kc910lib, strlen(kc910lib));
strncpy(kc910Bin, kc910Path, strlen(kc910Path));
strcat(javaOptionString, "=");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/com.ibm.ws.ejb.thinclient_8.5.0.jar:");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/com.ibm.ws.runtime.jar:");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/com.ibm.ws.orb_8.5.0.jar:");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/ojdbc6.jar:");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/providerutil.jar:");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/j2ee.jar:");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/src.jar:");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/junit.jar:");
strcat(javaOptionString, kc910Lib);
strcat(javaOptionString, "/ldap.jar:");
strcat(javaOptionString, classPath);
strcat(javaOptionString, ":");
strcat(javaOptionString, kc910Path);
strcat(javaOptionString, "/C2J2.jar");
options.optionString = javaOptionString;
args.options = &options;
args.ignoreUnrecognized = 0;
int rv;
jsize buf, num;
rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
if (rv < 0 || !env){
printf("\nUnable to launch KC000 JVM %d. Perhaps you are attempting to create JVM. Attempting to fetch existing JVM, if running",rv);
JNI_GetCreatedJavaVMs(jvm, buf, &num);
(**jvm)->GetEnv(*jvm, (void**)&env, JNI_VERSION_1_4);
}
else
printf("\nLaunched kc000 JVM!");
return env;
}
void communicateKc000(JNIEnv* env, kc0InputParms in, kc0ReturnParms **kc000outparms, int *retcode)
{
jclass init_class;
jmethodID init_method;
jstring inString, outString;
kc0ReturnParms **kc000out;
char returnCodeString[9];
int flag = 8, i = 0;
kc000out = kc000outparms;
inString = (*env)->NewStringUTF(env, in.inStream);
init_class = (*env)->FindClass(env, "shared/misc/kc000/Kc000");
init_method = (*env)->GetStaticMethodID(env, init_class, "init", "(Ljava/lang/String;)Ljava/lang/String;");
outString = (*env)->CallStaticObjectMethod(env, init_class, init_method, inString);
const char *rawString = (*env)->GetStringUTFChars(env, outString, 0);
jclass newExcCls = (*env)->FindClass(env, "java/lang/Throwable");
if(newExcCls == NULL)
{
/*printf("Exception not found\n");*/
}
else
{
(*env)->ExceptionDescribe(env);
}
strcpy((*kc000out)->inStream, rawString);
memset(returnCodeString, '\0', sizeof(returnCodeString));
strncpy(returnCodeString, rawString, 8);
printf("KC000 return code: [%s]\n", returnCodeString);
if(strlen(returnCodeString) > 0){
for(i = 0; i < 8; i++){
if(returnCodeString[i] == ' '){
flag = 0;
}
else{
flag = 8;
}
}
*retcode = flag;
}
else{
printf("Nothing returned in the return code of KC000. Setting this as an error.\n");
*retcode = 8;
}
}
最佳答案
我自己找到了问题的答案。以下是我为摆脱这种未定义行为所做的事情:
#include "jni_funct.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
JavaVM *jvm;
JNIEnv *env;
int invokeKc000(kc0InputParms kc0inp, kc0ReturnParms *kc0ret)
{
//JavaVM *jvm;
//JNIEnv *env;
kc0InputParms input;
kc0ReturnParms *kc000out;
char *kc910Path, *kc910Lib;
int retcode;
retcode = 8;
//Rest of the codes
我将 JNIEnv
和 JavaVM
设置为全局变量而不是本地变量。
我仍然不明白为什么堆栈分配会导致这种未定义的行为,而数据段分配不会导致这样的问题。我将发布有关此更改的另一个问题。
感谢@Martin Zabel 的提示。
关于c - JNI : call to a function where memset happens causes jvm-fetch to crash in the second call,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34514290/