2

概括

在使用 OCI_THREADED 选项设置 OCI 环境失败后尝试释放 OCIEnv 结构时,会发生分段错误(由于例如错误配置的 NLS_LANG 环境变量而失败)。

当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

堆栈跟踪

问题是使用 NULL 指针调用 __pthread_mutex_destroy。

#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

问题

目前我只是不释放该内存区域,但这不是一个好的解决方案。你怎么看,什么是好的解决方案?

4

1 回答 1

0

这看起来可能是“基本精简版”即时客户端中的一个错误,尽管我在 MOS 上看不到任何相关内容;但如果是这样的话OCIEnvCreate(),不是OCIHandleFree()我认为你建议的那样。

OCIEnv我见过的示例代码都没有尝试在遇到失败时清理OCIEnvCreate(); 它似乎总是退出,包括Oracle 自己的示例代码。自从您获得指向它的指针以来,该函数似乎正在创建OCIEnv结构,但可能尚未分配该结构的内部。由于它处于不确定状态,因此试图清理它可能是一项吃力不讨好的任务。因此,不打电话似乎应该没问题OCIHandleFree()

我能够在 Oracle Enterprise Linux 5.6 下使用适用于 Linux x86-64 的 11.2.0.3.0 Basic Lite 客户端包(来自TechNet 下载)重新创建问题。使用基本(非精简版)客户端包时,未发现问题 - 没有内存错误,也没有“无法初始化环境”消息,因此OCIEnvCreate()调用成功。但是它确实无法登录,无论如何这可能更合理。

这表明你不应该期望函数因为NLS_LANG值而失败——那部分看起来像一个错误。如果由于其他原因确实失败了,请不要尝试清理。我看不到任何暗示 SDK 不能与 Lite 包一起使用的东西,但是您正在强制失败,然后尝试清理超出规范,因此以前可能不会经常看到这种组合。但是,即使没有触发内存故障的清理,它看起来也是在错误的点上失败了。

要克服明显的错误并在正确的时间点出现故障,在登录期间,您可能需要切换到基本(非 Lite)客户端包。

于 2013-02-20T10:00:09.457 回答