如何快速动态加载经常重新生成的c代码?

2024-02-07

我希望能够动态生成 C 代码并将其快速重新加载到我正在运行的 C 程序中。

我在Linux上,这怎么办?

Linux 上的库 .so 文件可以在运行时重新编译和重新加载吗?

是否可以在不生成 .so 文件的情况下对其进行编译,编译后的输出是否可以以某种方式进入内存然后重新加载?我想快速重新加载编译后的代码。


你想做的事情是合理的,而我正是这样做的MELT http://gcc-melt.org/(一种扩展 GCC 的高级领域特定语言;MELT 通过用 MELT 编写的翻译器本身编译为 C)。

首先,在生成 C 代码(或许多其他源语言)时,一个好的建议是保留某种类型的抽象语法树 http://en.wikipedia.org/wiki/Abstract_syntax_tree(AST)在内存中。因此,首先构建生成的 C 代码的整个 AST,然后将其作为 C 语法发出。不要认为您的代码生成框架没有显式 AST(换句话说,使用一堆 printf 生成 C 代码是维护的噩梦,您希望有一些中间表示)。

其次,生成C代码的主要原因是为了利用良好的优化编译器(另一个原因是C的可移植性和普遍性)。如果您不关心生成代码的性能(并且 TCC 很快地将 C 编译成非常幼稚且缓慢的机器代码),您可以使用其他一些方法,例如使用一些 JIT 库,例如Gnu 闪电 http://www.gnu.org/software/lightning/(非常快速地生成缓慢的机器代码),Gnu Libjit http://www.gnu.org/software/dotgnu/libjit-doc/libjit.html or ASMJIT https://github.com/kobalicek/asmjit(生成的机器代码更好一点),LLVM http://llvm.org or GCCJIT https://gcc.gnu.org/onlinedocs/jit/(生成的机器代码很好,但生成时间与编译器相当)。

因此,如果您生成 C 代码并希望它快速运行,则 C 代码的编译时间不可忽略(因为您可能会 forkgcc -O -fPIC -shared创建一些共享对象的命令foo.so从你生成的foo.c)。根据经验,生成 C 代码比编​​译它花费的时间少得多(使用gcc -O)。在 MELT 中,C 代码的生成速度比 GCC 编译速度快 10 倍以上(通常快 30 倍)。但 C 编译器所做的优化是值得的。

一旦你发出你的 C 代码,将其编译分叉成.so共享对象,你可以dlopen它。别害羞,我的manydl.c http://starynkevitch.net/Basile/manydl.c示例演示了在 Linux 上您可以 dlopen 大量共享对象(数十万个)。真正的瓶颈是生成的 C 代码的编译。在实践中,你真的不需要dlclose在 Linux 上(除非您正在编写需要运行数月的服务器程序);未使用的共享模块实际上可以保留dlopen-ed 并且您主要是在泄漏进程地址空间(这是一种廉价的资源),因为其中大部分未使用.so将被换出。dlopen完成得很快,需要时间的是 C 源代码的编译,因为你确实希望由 C 编译器来完成优化。

您可以使用许多其他不同的方法,例如有一个字节码解释器并生成该字节码,使用 Common Lisp(例如 Linux 上的 SBCL,它动态编译为机器代码)、LuaJit、Java、MetaOcaml 等。

正如其他人所建议的,您不太关心编写 C 文件的时间,它实际上会保留在文件系统缓存中(另请参阅this https://stackoverflow.com/a/7880788/841108)。而且编写它比编译它要快得多,因此保留在内存中并不值得。使用一些tmpfs如果您担心 I/O 时间。

addenda

你问

可以图书馆吗.soLinux 上的文件被重新编译并re-运行时加载?

当然是的:您应该派生一个命令来从生成的 C 代码构建库(例如gcc -O -fPIC -shared generated.c -o generated.so,但你可以间接地做到这一点,例如通过运行make -j,特别是如果generated.so足够大以使其与拆分相关generated.c在几个 C 生成的文件中!)然后你动态加载你的库dlopen http://linux.die.net/man/3/dlopen(给出完整路径,例如/some/file/path/to/generated.so,并且可能是RTLD_NOW标志,到它),你必须使用dlsym找到里面的相关符号。别想re-加载(第二次)相同generated.so,更好地发出独特的generated1.c (then generated2.c等等...)C 文件,然后将其编译为unique generated1.so(第二次到generated2.so等...)然后dlopen它(并且这可以完成数十万次)。您可能希望在发出的generated*.c文件,一些构造函数 http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html将在以下位置执行的函数dlopen的时间generated*.so

您的基础应用程序应该定义一个关于一组的约定dlsym http://linux.die.net/man/3/dlsym-ed 名称(通常是函数)以及它们的调用方式。它应该只直接调用你的函数generated*.so thru dlsym-ed 函数指针。在实践中,您会决定例如每个generated*.c定义一个函数void dynfoo(int) and int dynbar(int,int)并使用dlsym with "dynfoo" and "dynbar"并通过函数指针调用这些(由dlsym)。您还应该定义如何以及何时这些的约定dynfoo and dynbar会被称为。您最好将您的基本应用程序链接到-rdynamic这样你的generated*.c文件可以调用您的应用程序函数。

You don't想要你的generated*.so to 重新定义 existing名称。例如,您不想重新定义malloc在你的generated*.c并期望所有堆分配函数神奇地使用您的新变体(这可能不起作用,即使起作用,也会很危险)。

你可能不会费心去dlclose动态加载的共享对象,除了在应用程序清理和退出时(但我根本不费心去dlclose)。如果你这样做dlclose一些动态加载的generated*.so文件中,请确保其中未使用任何内容:其中不存在任何指针,甚至不存在调用帧中的返回地址。

附: MELT 翻译器目前已将 57KLOC 的 MELT 代码翻译为近 1770KLOC 的 C 代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何快速动态加载经常重新生成的c代码? 的相关文章

  • 尝试了解使用服务打开对话框

    我已经阅读了有关使用 mvvm 模式打开对话框的讨论 我看过几个使用服务的示例 但我不明白所有部分如何组合在一起 我发布这个问题寻求指导 以了解我应该阅读哪些内容 以更好地理解我所缺少的内容 我将在下面发布我所拥有的内容 它确实有效 但从我
  • Grpc - 将消息从一个客户端发送到连接到同一服务器的另一个客户端

    是否可以将消息从一个客户端发送到连接到同一服务器的另一个客户端 我想将数据从一个客户端发送到服务器然后发送到特定客户端 我想我需要获取客户端 ID 但我不知道如何获取此 ID 以及如何从服务器将此消息发送到该客户端 我这里有一个样本 这是一
  • Rx.NET 中是否有一个Subject 实现,其功能类似于BehaviourSubject,但仅在值发生更改时才发出?

    有没有Subject https learn microsoft com en us previous versions dotnet reactive extensions hh229699 v vs 103 Rx NET 中的实现在功能
  • 前向声明类型和“已声明为类类型的非类类型”

    我对以下代码有问题 template
  • 当事件button.click发生时,如何获取按钮名称/标签?

    我以编程方式制作按钮并将它们添加到堆栈面板中 以便每次用户导航到页面时按钮都会发生变化 我正在尝试做这样的事情 当我单击创建的按钮时 它将获取按钮的标签并转到正确的页面 但是 我无法使用 RoutedEventHandler 访问按钮元素
  • java中如何重新初始化int数组

    class PassingRefByVal static void Change int pArray pArray 0 888 This change affects the original element pArray new int
  • 如何在 C# Designer.cs 代码中使用常量字符串?

    如何在 designer cs 文件中引用常量字符串 一个直接的答案是在我的 cs 文件中创建一个私有字符串变量 然后编辑 Designer cs 文件以使用此变量 而不是对字符串进行硬编码 但设计者不喜欢这样抛出错误 我明白为什么这行不通
  • 如何使用 Regex.Replace 从字符串中删除数字?

    我需要使用Regex Replace从字符串中删除所有数字和符号 输入示例 123 abcd33输出示例 abcd 请尝试以下操作 var output Regex Replace input d string Empty The d标识符
  • C++中判断unicode字符是全角还是半角

    我正在编写一个终端 控制台 应用程序 该应用程序应该包装任意 unicode 文本 终端通常使用等宽 固定宽度 字体 因此要换行文本 只需计算字符数并观察单词是否适合一行并采取相应的操作 问题是 Unicode 表中的全角字符在终端中占用了
  • 在 VS 中运行时如何查看 C# 控制台程序的输出?

    我刚刚编写了一个名为 helloworld 的聪明程序 它是一个 C NET 4 5 控制台应用程序 在扭曲的嵌套逻辑迷宫深处 使用了 Console WriteLine 当我在命令行运行它时 它会运行并且我会看到输出 我可以执行其他命令并
  • 是否使用 C# 数据集? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我对 C 中的数据集概念有点困惑 编码 ASP NET 站点 但这并不重要 在我的阅读中 我了解到它们 本质上 用作我的应用程序和我的
  • 不可变类与结构

    以下是类与 C 中的结构的唯一区别 如果我错了 请纠正我 类变量是引用 而结构变量是值 因此在赋值和参数传递中复制结构的整个值 类变量是存储在堆栈上的指针 指向堆上的内存 而结构变量作为值存储在堆上 假设我有一个不可变的结构 该结构的字段一
  • memcpy/memmove 到联合成员,这是否设置“活动”成员?

    重要说明 一些评论者似乎认为我是从工会抄袭的 仔细看memcpy 它从普通旧地址复制uint32 t 它不包含在联合中 另外 我正在复制 通过memcpy 到工会的特定成员 u a16 or u x in a union 不直接到整个联盟本
  • C++ - 多维数组

    处理多维数组时 是否可以为数组分配两种不同的变量类型 例如你有数组int example i j 有可能吗i and j是两种完全不同的变量类型 例如 int 和 string 听起来您正在寻找 std vector
  • C++:二叉树所有节点值的总和

    我正在准备面试 我被一个二叉树问题困住了 我们如何计算二叉树所有节点中存在的值的总和 优雅的递归解决方案 伪代码 def sum node if node NULL return 0 return node gt value sum nod
  • Java时区混乱

    我正在运行 Tomcat 应用程序 并且需要显示一些时间值 不幸的是 时间快到了 还有一个小时的休息时间 我调查了一下 发现我的默认时区被设置为 sun util calendar ZoneInfo id GMT 08 00 offset
  • 在 System.Type 上使用条件断点时出错

    这是函数 public void Init System Type Type this Type Type BuildFieldAttributes BuildDataColumns FieldAttributes 我在第一行设置了一个断点
  • 我可以使用 lambda 函数或 std::function 对象来代替函数指针吗?

    我有一个需要使用的库 它定义了以下内容 typedef void CallbackFunction const int i 并且有一个注册回调的函数 如下所示 void registerCallback CallbackFunction p
  • MySqlConnectionStringBuilder - 使用证书连接

    我正在尝试连接到 Google Cloud Sql 这是一个 MySql 解决方案 我能够使用 MySql Workbench 进行连接 我如何使用 C 连接MySqlConnectionStringBuilder 我找不到提供这三个证书的
  • 当用户更改 Windows 中的语言键盘布局时如何通知?

    I want to show a message to user when the user changes the language keyboard layout of Windows for example from EN to FR

随机推荐