在使用OCI_THREADED选项(由于例如错误配置的NLS_LANG环境变量导致的故障)导致OCI环境设置失败后尝试释放OCIEnv结构时发生分段错误。
当OCIEnvCreate在没有 OCI_THREADED选项的情况下调用时,示例代码不会崩溃,它会按预期工作。
#include <oci.h>;
#include <stdio.h>
#include <string.h>
int my_connect(const char *username, const char *password, const char *sid)
{
OCIEnv *env = NULL;
OCIError *err = NULL;
OCISvcCtx *svc = NULL;
if ( OCIEnvCreate(&env,
OCI_THREADED,
(dvoid *)0,
0,
0,
0,
(size_t)0,
(dvoid **)0) )
{
fprintf(stderr, "unable to initialize environment\n");
if ( env )
{
printf("env:[%p]\n", env);
OCIHandleFree(env, OCI_HTYPE_ENV); // segfault.
}
return -1;
}
printf("env:[%p]\n", env);
if ( OCIHandleAlloc((dvoid *)env,
(dvoid **)&err,
OCI_HTYPE_ERROR,
(size_t)0,
(dvoid **)0) )
{
fprintf(stderr, "unable to alloc error handlers\n");
goto error;
}
if ( OCIHandleAlloc((dvoid *) env,
(dvoid **) &svc,
OCI_HTYPE_SVCCTX,
(size_t) 0,
(dvoid **)0) )
{
fprintf(stderr, "unable to allocate service handlers\n");
goto error;
}
if ( OCILogon(env,
err,
&svc,
(CONST OraText *) username,
strlen(username),
(CONST OraText *) password,
strlen(password),
sid,
strlen(sid)
) )
{
fprintf(stderr, "login failed\n");
goto error;
}
printf("logged in\n");
if ( OCILogoff (svc, err) )
{
fprintf(stderr, "logoff failed\n");
goto error;
}
printf("logged out\n");
error:
if ( err )
OCIHandleFree(err, OCI_HTYPE_ERROR);
if ( svc )
OCIHandleFree(svc, OCI_HTYPE_SVCCTX);
if ( env )
OCIHandleFree(env, OCI_HTYPE_ENV);
return 0;
}
int main()
{
return my_connect("test_user", "qqq123", "XE");
}
export NLS_LANG=x
问题是__pthread_mutex_destroy是用NULL指针调用的。
#0 __pthread_mutex_destroy (mutex=0x0) at pthread_mutex_destroy.c:28
#1 0x00007ffff585e6e0 in sltsmxd () from /lib/libclntsh.so.11.1
#2 0x00007ffff56a147c in kpufhndl0 () from /lib/libclntsh.so.11.1
#3 0x00007ffff56a0185 in kpufhndl () from /lib/libclntsh.so.11.1
#4 0x00007ffff567cac1 in OCIHandleFree () from /lib/libclntsh.so.11.1
#5 0x0000000000400a0c in my_connect (username=0x400dd1 "test_user", password=0x400dca "qqq123", sid=0x400dc7 "XE") at test2.c:24
#6 0x0000000000400c27 in main () at test2.c:84
Basic Lite Package Information
Thu Oct 4 13:00:49 UTC 2007
Client Shared Library 64-bit - 11.1.0.6.0
System name: Linux
Release: 2.6.9-34.0.1.0.11.ELsmp
Version: #1 SMP Mon Dec 4 22:20:39 UTC 2006
Machine: x86_64
Linux 3.2.0-37-generic #58-Ubuntu SMP Thu Jan 24 15:28:10 UTC 2013 x86_64 GNU/Linux
Distributor ID: Ubuntu
Description: Ubuntu 10.04.4 LTS
Release: 10.04
Codename: lucid
目前我还没有释放该内存区域,但这不是一个好的解决方案。 您怎么看?什么是好的解决方案?
答案 0 :(得分:0)
这看起来可能是'Basic Lite'即时客户端中的一个错误,但我看不出任何与MOS有关的内容;但如果是这样,那么在OCIEnvCreate()
,而不是OCIHandleFree()
正如我认为你建议的那样。
我见过的示例代码都没有尝试在遇到OCIEnv
失败时清理OCIEnvCreate()
;似乎总是退出,包括Oracle's own example code。看起来这个函数正在创建OCIEnv
结构,因为你得到了一个指向它的指针,但可能没有分配它的内部结构。由于它处于一个不确定的状态,试图清理它可能是一个吃力不讨好的任务。因此,似乎不应该拨打OCIHandleFree()
。
我能够在Oracle Enterprise Linux 5.6下使用Linux x86-64的11.2.0.3.0 Basic Lite客户端软件包(来自TechNet downloads)重新创建问题。使用Basic(非Lite)客户端软件包时,没有看到问题 - 没有内存故障,也没有“无法初始化环境”消息,因此OCIEnvCreate()
调用成功。然而它确实无法登录,无论如何这可能更合理。
这表明您不应该期望函数因NLS_LANG
值而失败 - 该部分看起来像一个bug。如果由于某些其他原因确实失败了,那么不要试图清理。我看不出任何暗示SDK不能与Lite包一起使用的东西,但是你强迫失败然后试图清理超出常规,所以之前可能不会经常看到这种组合。尽管如此,即使没有清理触发内存故障,它看起来就像是在错误的点上失败了。
要通过明显的错误并在正确的位置获取失败,在登录期间,您可能需要切换到Basic(非Lite)客户端软件包。