首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >C共享库:静态变量初始化+进程间的全局变量可见性

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

Stack Overflow用户
提问于 2012-01-06 10:38:24
回答 5查看 3.4K关注 0票数 0

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

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

  • 标准malloc、calloc等功能
  • malloc、calloc等的专门版本

我想出了一个解决这个问题的潜在方法(在这里的一些人的帮助下)。到目前为止,仍有一些灰色地带,我想就我的建议提出一些意见。

这就是我打算如何执行修改:

  1. 将对malloc/calloc等的现有调用替换为my_malloc/my_calloc等。这些新函数将调用正确分配的函数指针,而不是调用硬编码函数名。
  2. 为共享库提供一种机制,用于初始化my_malloc等使用的函数指针,以指向标准的C内存mgmt例程--这允许我向依赖于共享库的应用程序提供向后兼容性--因此它们也不必进行修改。在C++中,我可以通过使用静态变量初始化(例如)来做到这一点--我不确定在C中是否可以使用相同的“模式”。
  3. 引入一个新的幂等函数initAPI(type)函数,它由需要在共享libray中使用不同mgmt例程的应用程序调用(在启动时)。initAPI()函数将内存mgmt分配给适当的函数。

显然,如果我可以限制谁可以调用initAPI()或者何时调用它(例如,函数不应该在API调用到库之后调用),这将改变内存mgmt例程,这将是更好的选择。因此,我想限制它的名称和由谁。这是一个访问问题,可以通过C++中的私有方法来解决,我不知道如何在C中这样做。

上面第2和第3段的问题可以在C++中解决,但是我不得不使用C语言,所以我想用C来解决这些问题。

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

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

*注:如果我天真地低估了我上面描述的“架构”在未来可能发生的线程问题,请有人提醒我!

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

EN

回答 5

Stack Overflow用户

发布于 2012-01-06 17:48:50

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

您已经指出了Clinux,假设您也在使用GNU可能是安全的。

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

代码语言:javascript
代码运行次数:0
运行
复制
void __attribute__ ((constructor)) initAPI(void);

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

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

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

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

代码语言:javascript
代码运行次数:0
运行
复制
#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;
}

我建议阅读杰伊康罗德博客文章,标题为http://www.jayconrod.com/cgi/view_post.py?23,以获得关于用调用自己的包装函数替换动态库中函数调用的其他信息。

票数 3
EN

Stack Overflow用户

发布于 2012-01-06 11:52:51

-1由于缺乏具体问题。这篇文章很长,可以写得更简洁,而且不包含一个问号。

现在,为了解决您的问题:

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

在C语言中,不能使用restrict1调用函数。它可以被任何知道它的名字或者有指针的人调用。您可以编写initAPI()代码,这样如果它不是第一个调用的库函数,它就会明显地中止程序(崩溃)。你是图书馆的作者,你设定了游戏规则,你对那些不尊重规则的程序员没有义务。

1您可以用静态方式声明函数,这意味着它只能由同一翻译单元中的代码调用;任何设法获得指向它的指针的人仍然可以通过指针调用它。这些函数不是从库中“导出”的,所以这不适用于您的场景。

票数 1
EN

Stack Overflow用户

发布于 2012-01-06 14:28:06

为此目的:

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

  • 标准malloc、calloc等功能
  • malloc、calloc等的专门版本

在Linux上使用动态库非常简单,并且不需要复杂的方案(也不需要@ugoren建议的LD_PRELOADdlopen )。

当您想提供malloc和朋友的专门版本时,只需将这些例程链接到您的主要可执行文件。瞧:您现有的共享库将从那里获取它们,不需要任何修改。

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

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

更新:

进一步考虑一下,我认为你的建议行不通。

是的,它可以工作(我每天都使用这个功能,将tcmalloc链接到我的主要可执行文件中)。

当您的共享库(提供API的库)调用malloc“幕后”时,它得到了(可能有几个) malloc实现?第一个是动态链接器可见的。如果您将一个malloc实现链接到a.out中,那么这就是其中的一个。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8756540

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档