C++动态库环境变量的传递

Posted 李迟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++动态库环境变量的传递相关的知识,希望对你有一定的参考价值。

本文研究C++中应用程序和动态库关于环境变量传递的问题。

问题提出

某工程使用到一动态库,内部通过环境变量来控制是否打印某些执行过程的日志。该工程程序在很多机器上运行正常。但笔者机器上无法达到预期,为了研究这个问题,基于以前的动态库工程,以重现问题。

工程代码

工程比较简单,分应用程序和动态库,但源码在同一目录,只是通过 Makefile 编译得到不同的目标文件。在应用程序使用setenv设置环境变量,动态库内使用getenv获取环境变量,再修改,最后应用程序读取。

动态库的加载使用dlopen函数,调用函数使用dlsym,这样能分离动态库,编译应用程序时不需要依赖so文件 。

所有工程源码如下。

dl.h头文件:

#ifndef DL_H
#define DL_H

#ifdef __cplusplus
extern "C" 
#endif

// 使用结构体管理库函数
typedef struct 
    const char *name; // 名称
    // 函数
    int (*GetVersion)(char *version);
    int (*Foo)();
    int (*Bar)();
 DL_API_t;

// 单独的函数
int foo();

int bar();

int env_test();

extern DL_API_t gAPI;

#ifdef __cplusplus

#endif

#endif

dl.cpp实现代码:

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "dl.h"

int GetVersion(char *version)

    int ver = 10;
    sprintf(version, "ver: 0.%d", ver);
    return 0;


int Foo()

    printf("Foo...\\n");
    return 0;


int Bar()

    printf("Bar...\\n");
    return 0;


DL_API_t gAPI = 
    .name = "MyLib",
    GetVersion,
    Foo,
    Bar,
;
///

int foo()

    printf("foo...\\n");
    return 0;


int bar()

    printf("bar...\\n");
    return 0;


int env_test()

    printf("get env in so lib...\\n");
    char* value = getenv("FOOBAR");
    printf("FOOBAR: %s\\n", value); // 不存在的环境变量

    value = getenv("FOO");
    printf("FOO: %s\\n", value);
    
    value = getenv("BAR");
    printf("BAR: %s\\n", value);

    setenv("BAR", "bar 250", 1);

    return 0;

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>

#include <string.h>
#include <errno.h>

#include "dl.h"

typedef int (*pfoo_t)();

pfoo_t pfoo;

int mysetenv(const char* env, const char* value)

    if(setenv(env, value, 1) != 0)
	
		printf("setenv %s failed %d: %s\\n", env, errno, strerror(errno));
	
    return 0;


int main(void)

    void* handle = NULL;
    char version[64] = 0;
    printf("so test...\\n");
    
    DL_API_t* aLib = NULL;

    // 在加载动态库前设置
    //mysetenv("FOO", "foo");
    //mysetenv("BAR", "bar");
    
    handle = dlopen("./libfoobar.so", RTLD_LAZY); // 必须加路径
    if (handle == NULL)
    
        printf("open failed.\\n");
        return -1;
    
    
    aLib = (DL_API_t*)dlsym(handle, "gAPI");
    if (!aLib)
    
        printf("dlsym failed: %s\\n", dlerror());
        return -1;
    
    
    aLib->GetVersion(version);
    printf("ver: %s\\n", version);
/*
    if (aLib->Foo)
    
        aLib->Foo();
    
    
    // 另一方式
    pfoo = (pfoo_t)dlsym(handle, "foo");
    if (pfoo)
    
        pfoo();
    
    // 注:foo和bar的形式一样,可复用指针
    pfoo = (pfoo_t)dlsym(handle, "bar");
    if (pfoo)
    
        pfoo();
    
*/
    mysetenv("FOO", "foo");
    mysetenv("BAR", "bar");
    
    // 注:复用指针
    pfoo = (pfoo_t)dlsym(handle, "env_test");
    if (pfoo)
    
        pfoo();
    
    
    char* value = getenv("BAR");
    printf("BAR in main: %s\\n", value);
    
    dlclose(handle);
    
    return 0;

原始工程是为了测试动态库存在不同版本的接口函数的,现加了环境变量的测试。

测试

测试结果:

$ ./a.out 
so test...
ver: ver: 0.10
get env in so lib...
FOOBAR: (null)
FOO: foo
BAR: bar
BAR in main: bar 250

从结果看出,应用程序和动态库之间是可以通过环境变量传递数据的。

小结

当初发现问题时做了尝试,先在命令行中设置好环境变量,然后再运行程序,是没问题。后和晨哥昌哥讨论,猜测需要在动态库加载之前设置环境变量,后测试果然如是。而在上述代码测试中,不管在哪个阶段设置环境变量,都没问题。这其中有什么玄机,暂时不得而知。

本来想在 golang 和 C++ 程序中使用环境变量交互数据的,但测试后发现也是不行。所以放弃了。

以上是关于C++动态库环境变量的传递的主要内容,如果未能解决你的问题,请参考以下文章

如何查看静态库内容 Unix/Linux

多版本动态库的变量测试

多版本动态库的变量测试

C++ 静态库中的共享全局变量:Linux

golang 调用 c++动态库(winlinux环境)

传递一个环境。动态变量到应用程序模块,在运行时?