繁体   English   中英

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

[英]C Shared library: static variable initialization + global variable visibility among processes

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

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

  • 标准的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()函数将内存管理函数分配给适当的函数。

显然,如果我可以限制谁可以调用initAPI()或何时调用它会是更好的选择-例如,在对库进行API调用后不应调用该函数-因为这将更改内存mgmt例程。 因此,我想限制在何处调用它以及由谁调用。 这是一个访问问题,可以通过在C ++中将方法设为私有来解决,但我不确定如何在C中执行此操作。

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

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

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

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

顺便说一句,我正在Linux(Ubuntu)上构建库

由于我不确定要问的是什么问题,因此我将尝试提供可能有用的信息。

您已经指出了 ,可以肯定地假设您也在使用GNU工具链。


GCC提供了一个构造 函数功能属性 ,该属性使函数在执行进入main()之前自动被调用。 您可以使用它来更好地控制何时调用库初始化例程initAPI()

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

对于库初始化,如果在运行时加载库,则在dlopen()返回之前执行构造函数例程;如果在加载时加载库,则在启动main()之前执行构造函数例程。


GNU链接器具有--wrap <symbol>选项,该选项使您可以为系统功能提供包装器。

如果与--wrap malloc链接, --wrap mallocmalloc()引用将重定向到__wrap_malloc() (由您实现),而对__real_malloc()引用将重定向到原始malloc() (因此可以在包装器中调用它)实现)。

除了使用--wrap malloc选项提供对原始malloc()的引用之外,您还可以使用dlsym()动态加载指向原始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的博客文章“ 教程:Linux中的函数插入”,以获取有关使用对您自己的包装函数的调用替换对动态库中函数的调用的其他信息。

-1为缺乏具体问题。 文本很长,本来可以写得很简洁,并且不包含单个问号。

现在解决您的问题:

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

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

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

实现此目的:

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

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

在Linux上使用动态库是微不足道的 ,并且不需要你炮制复杂的方案(也不是LD_PRELOADdlopen由@ugoren建议)。

当您想要提供malloc和friends的专用版本时,只需将这些例程链接到您的主可执行文件中即可。 瞧,您现有的共享库将在此处进行提取, 无需进行任何修改

您也可以在例如libmymalloc.so构建专用的malloc ,并将该库放在libc 之前的链接行上,以实现相同的结果。

动态加载器将使用它可以看到的第一个malloc ,并从a.out开始搜索列表,并以链接命令行上列出的相同顺序继续搜索其他库。

更新:

经过进一步的思考,我认为您的建议不会奏效。

是的,它工作(我每天都在使用该功能,通过连接tcmalloc到我的主要可执行文件)。

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

要求您的初始化函数很简单:

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

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

基本上,这就是诸如valgrind之类的工具,它们代替了malloc。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM