C共享库:静态变量初始化+进程间的​​全局变量可见性

时间:2012-01-06 10:38:25

标签: c linux ubuntu shared-libraries

我想修改现有的共享库,以便根据使用共享库的应用程序使用不同的内存管理例程。

(目前)将有两个内存管理例程系列:

  • 标准的malloc,calloc等函数
  • malloc,calloc等的专用版本

我想出了解决这个问题的潜在方法(在SO的帮助下)。目前还有一些灰色地带,我想对我的建议提出一些反馈意见。

这是我打算实施修改的方式:

  1. 用my_malloc / my_calloc等替换对malloc / calloc等的现有调用。这些新函数将调用正确分配的函数指针,而不是调用硬编码函数名。

  2. 为共享库提供一种机制来初始化my_malloc等使用的函数指针,指向标准的C内存mgmt例程 - 这使我可以向依赖于这个共享库的应用程序提供向后兼容性 - 所以它们不必修改。在C ++中,我可以通过使用静态变量初始化(例如)来完成此操作 - 我不确定在C中是否可以使用相同的“模式”。

  3. 引入一个新的幂等函数initAPI(type)函数,该函数由需要在共享库中使用不同mem mgmt例程的应用程序调用(在启动时)。 initAPI()函数将内存mgmt func ptrs分配给适当的函数。

  4. 显然,如果我可以限制谁可以调用initAPI()或者调用它,那将是更好的选择 - 例如,在对库调用API之后不应该调用该函数 - 因为这将改变内存mgmt例程。所以我想限制它的调用地点和由谁调用。这是一个访问问题,可以通过在C ++中将该方法设为私有来解决,我不知道如何在C中执行此操作。

    上面的2和3中的问题可以在C ++中轻松解决,但是我被限制使用C,所以我想用C解决这些问题。

    最后,假设在初始化过程中可以正确设置函数指针,如上所述 - 我有第二个问题,关于共享库中全局变量的可见性,使用共享库的不同进程。函数指针将被实现为全局变量(我现在不太关心线程安全 - 尽管我设想在某些时候使用互斥锁来包装访问)*并且使用共享库的每个应用程序都不应该干扰内存管理例程用于使用共享库的另一个应用程序。

    我怀疑是使用shlib在进程之间共享的代码(不是数据) - 但是,我希望确认 - 最好是使用支持该断言的链接。

    *注意:如果我天真地淡化了由于上面描述的“架构”而可能在将来发生的线程问题,有人请提醒我!..

    BTW,我正在Linux(Ubuntu)上构建库

5 个答案:

答案 0 :(得分:3)

由于我不完全确定所提出的问题是什么,我会尝试提供可能有用的信息。

您已指出,假设您也使用GNU工具链可能是安全的。


GCC提供了一个构造函数 function attribute,它导致在执行main()之前自动调用函数。您可以使用它来更好地控制何时调用库初始化例程initAPI()

void __attribute__ ((constructor)) initAPI(void);

在库初始化的情况下,如果在运行时加载库,或者在加载时加载库,则在dlopen()启动之前,在main()返回之前执行构造函数例程。


GNU链接器有一个--wrap <symbol>选项,允许您为系统函数提供包装器。

如果您与--wrap malloc相关联,则对malloc()的引用将重定向到__wrap_malloc()(您实施),对__real_malloc()的引用将重定向到原始malloc() (所以你可以在你的包装器实现中调用它。)

您可以使用dlsym()动态加载指向原始--wrap malloc的指针,而不是使用malloc()选项提供对原始malloc()的引用。您无法直接从包装器调用原始malloc(),因为它将被解释为对包装器本身的递归调用。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>

void * malloc(size_t size) {
   static void * (*func)(size_t) = NULL;
   void * ret;

   if (!func) {
      /* get reference to original (libc provided) malloc */
      func = (void *(*)(size_t)) dlsym(RTLD_NEXT, "malloc");
   }

   /* code to execute before calling malloc */
   ...

   /* call original malloc */
   ret = func(size);

   /* code to execute after calling malloc */
   ...

   return ret;
}

我建议阅读标题为Jay Conrod'sTutorial: Function Interposition in Linux博文,了解有关通过调用您自己的包装函数替换对动态库中函数的调用的其他信息。

答案 1 :(得分:1)

-1缺乏具体问题。文本很长,本来可以写得更加简洁,而且不包含任何一个问号。

现在解决您的问题:

共享库的静态数据(您称之为“全局变量”)是按进程的。一个进程中的全局变量不会干扰另一个进程中的全局变量。不需要互斥锁。

在C中,你不能限制[1]谁可以调用一个函数。任何知道其名称或指向它的人都可以调用它。如果它不是第一个被调用的库函数,您可以对initAPI()进行编码,使其可见地中止程序(崩溃)。你是图书馆作家,你设定了游戏规则,你对没有遵守规则的编码员没有义务。

[1]您可以使用static声明该函数,这意味着它只能通过同一翻译单元中的代码按名称调用;它仍然可以被任何设法获取指针的人通过指针调用。此类函数不会从库中“导出”,因此不适用于您的场景。

答案 2 :(得分:1)

实现这一目标:

  

(目前)将有两个内存管理例程系列:

     
      
  • 标准的malloc,calloc等函数
  •   
  • malloc,calloc等的专用版本
  •   

Linux上的动态库是琐事,而需要您编制的复杂方案(建议LD_PRELOADdlopen由@ugoren)。

如果要提供malloc和朋友的专用版本,只需将这些例程链接到主可执行文件中即可。 Voila:您现有的共享库将从那里获取它们,无需修改

您还可以将专门的malloc构建到例如libmymalloc.so,并在 libc之前将该库放在链接行上,以获得相同的结果。

动态加载程序将使用它可以看到的第一个malloc,并从a.out开始搜索列表,然后按照它们在链接命令行上列出的顺序搜索其他库。

更新:

  

经过进一步的反思,我认为你的建议不会起作用。

是的,它工作(我通过将tcmalloc链接到我的主要可执行文件中,每隔日使用该功能

当你的共享库(提供API的那个)在幕后调用malloc时,它会得到哪些(可能是几个)malloc实现? 第一个动态链接器可见的。如果您将malloc实施链接到a.out,那将是那个

答案 3 :(得分:0)

您可以轻松地要求初始化函数为:

  • 从主线程调用
  • 客户端可能只调用一次
  • 并且客户端可以通过参数
  • 提供可选的函数指针

答案 4 :(得分:0)

如果不同的应用程序在不同的进程中运行,那么使用动态库非常简单 该库可以简单地调用malloc()和free(),而想要覆盖它的应用程序可以加载另一个库,并为这些库提供替代实现。
这可以使用LD_PRELOAD环境变量来完成 或者,如果您的库加载了dlopen(),则首先加载malloc库。

这基本上是替换malloc等工具,例如valgrind。