【发布时间】:2018-02-10 19:49:03
【问题描述】:
问题
我正在开发一个大量使用全局变量的大型 C 项目 (C99)(我知道,我知道)。该程序运行良好,但它最初设计为运行一次并退出。
因此,它依赖于它的全局/静态内存用 0(或它声明的任何值)进行初始化,并且在运行时它会修改这些变量(就像大多数程序一样)。
但是,我不想在完成时退出,而是想再次运行该程序。我想制作一个对这个大型程序具有控制权和可见性的父程序。完全了解正在运行的程序非常重要。
该解决方案需要在 macOS、Linux 和 Windows 上运行。
我考虑过:
1。分叉它
制作一个充当“shell”的小包装程序,并根据需要执行大程序。
优点
- 操作系统会努力将内存重置为正确的值
- 保证按预期运行
缺点
- 失去对程序的了解
- 在运行时无法从包装器检查执行程序的内存,启动前更难调整设置,更难收集运行时信息
- 需要实现一个系统来将内部数据输入/输出程序,可能会涉及大量代码
- 统一体验更难(共享 GUI 窗口等)
2。手动识别关键结构
仔细阅读源代码,多次运行程序,等待程序因健全性检查或错误的内存访问而崩溃。
优点
- 简单易做
- 容易上手
- 高可见性、代码共享和统一
缺点
- 不是每一个案例都抓到,非常拼凑
- 耗时
3。重构
将所有全局变量收集到memset 的单个结构中,为使用值初始化的变量创建初始化程序。根据具体情况处理静态数据。
优点
- 概念上简单的大锤方法
- 高可见性、代码共享和统一
缺点
- 非常耗时,代码库很大,几乎可以触及所有内容
4。魔杖
告诉操作系统重新初始化全局/静态内存。如果我需要保存一个值,我会将它存储在本地,然后在完成后重写它。
优点
- 非常完美:)
缺点
- 不存在 (?)
- 非常黑魔法
- 可能不是跨平台
- 可能会激怒第 3 方库
我现在在做什么
我现在使用选项 2,只是通过代码,依靠程序崩溃并为我指明正确的方向。
我想说这种方法已经让我完成了大约 80% 的工作。我已经确定并重新初始化了足够多的东西,程序或多或少可以重新运行。它并没有我想象的那么普遍,它给了我很大的希望。
偶尔会发生奇怪的事情或者它没有按预期运行,但它也不会崩溃。这使得追踪它变得更加困难。
我只需要一些东西来让我最后 20%。可能是某种静态分析工具,或者可以帮助我查看源代码并查看全局变量的位置。
【问题讨论】:
-
函数中的静态变量可能真的有问题——你甚至可能争辩说它们应该成为文件范围。使用方法 2 扩展为 “对于定义任何全局变量的每个文件,添加一个
void filename_reset(void)函数将所有全局变量重置为默认值”。 如果它不为零,则需要一个可以在修改后的版本上复制的非默认数据的常量版本。不要跳过任何全局变量——直到你知道你可以安全地这样做。然后安排生成对filename1_reinit(); filename2_reinit();等的一系列调用——每个文件一个调用。 -
选项号 1 是唯一真正的选项。选项 2 要求您拥有进入可执行文件的每段代码的源代码。这包括您可能拥有的 3rd 方库。但它还包括 C 运行时和 C 标准库,我敢肯定你没有。
-
@user3386109:标准 C 库不会缓存太多,除非您计算语言环境设置(并且重新启动的代码可能需要考虑是否设置了语言环境 - 但可能会说“不” )。 C 库之外的库是另一回事——那里很容易出现问题。 POSIX 库中非线程安全的函数可能会出现问题——例如
getpwent()。 -
@JonathanLeffler
errno例如。由malloc维护的信息。有关打开文件的信息。即使是argv指向的字符串,程序也可以更改。程序退出时全部清理,但如果您只是告诉程序重新开始,则不会清理。 -
@user3386109: ... 内存已分配但未释放;环境变量集(虽然不是标准 C 的一部分)。有足够的可能性重新启动不是为重新启动而设计的软件(通过使用太多全局变量并且通常不清理资源)是非常有问题的。
标签: c memory static initialization global