在讲如何实现之前,我们首先要了解什么是 热重载
热重载 C 这是一篇挺好的文章,可以帮助大家了解相关概念(尤其是动态链接部分,本文并不打算讲解这部分,本文可以看成是对这篇文章例子的补充)
按照我的理解,热重载 这个名字其实就能够解释了,就是 重新载入 '热' 的部分,重新载入 好理解,那什么是 热 呢?其实我也不太明白,它应该是一个抽象的概念,用来表示那些 频繁引用 的部分
比如你的一个 switch 语句,其中一个 case 标签经常命中,那么他就是 热
按照字面意思理解了之后,他有什么用呢
其中一个重要的作用就是 立即反映,比如说你正在调试一个c程序,然后修改了源代码中的某个地方,ok,重新编译,重新运行,这样一来一回其实还挺烦的
那如果你将某个 频繁更改 的代码部分看作是一个 库,一个 热 的部分,然后使用 热重载 技术,这样你就能够在 程序不重启的前提下,即时观察到更改带来的效果
接下来,我们来一个简单的例子,带大家体验一下
本次实验基于以下环境:
- 操作系统:Ubuntu22.04
- 编译器:gcc 11.4.0
首先上场的是,作为 热 的部分的代码,也就是我们将要 频繁修改 的代码部分
// counter.c
#include <stdio.h>
void counter(void)
{
int end = 10;
printf("end=%d -> ", end);
for (int i = 0; i < end; i++)
printf("%d ", i);
printf("\n");
}
这是一个 counter,就是打印 [0, end) 的数字,然后我们将要修改 end 的值,并希望能够 即时 看到打印数字的变化
这部分代码是 加载模块 和 卸载模块,主要使用了 动态链接 的知识,不了解的同学,还请阅读开头推荐的文章,我这里就不细讲了
原理就是,先 卸载旧的模块(如果有的话),然后 system("make") 执行的时候,将代码 编译成动态库,这样能够保持 热 的部分是最新的,最后再 加载新的模块
// demo.c
static void unload_module(void *handle)
{
dlclose(handle);
}
static void *load_module(const char *path)
{
static void *handle = NULL;
// unload the old module
if (handle != NULL) unload_module(handle);
system("make");
handle = dlopen(path, RTLD_LAZY);
if (handle == NULL) {
fprintf(stderr, "dlopen error: %s\n", dlerror());
return NULL;
}
return handle;
}
## Makefile
libcounter.so: counter.c
gcc -shared -fPIC -o libcounter.so counter.c
最后是主程序部分,首先使用fgetc来阻塞程序,方便我们 “暂停” 修改代码,然后就是 加载模块,接着是 加载模块中的符号,也就是counter函数,最后是 执行函数 counter()
// demo.c
typedef void (*counter_func)(void);
int main(void)
{
char buffer[128];
while (1) {
fgetc(stdin);
void *handle = load_module("./libcounter.so");
counter_func counter = dlsym(handle, "counter");
if (counter == NULL) {
fprintf(stderr, "dlsym error: %s\n", dlerror());
unload_module(handle);
return 1;
}
counter();
}
return 0;
}
到这里我们已经讲解了代码的大致流程,接下来看看效果吧
首先这里是编译主程序,然后给大家先解释下这个图片,上面窗口是我们的 counter.c 代码,我们会修改 int end = 10; 这一行,下面是我们的 终端,打印的结果会呈现在这里

开始执行,let's go
int end = 10;

int end = 20;

int end = 1;

到这里,希望你对 如何在c中实现热重载技术 有了一定的想法,然后就去不断实践它吧
posted on
浙公网安备 33010602011771号