我正在处理的应用程序的一部分是一个简单的基于pthread的服务器,它通过TCP / IP套接字进行通信。我在C中编写它是因为它将在内存受限的环境中运行。我的问题是:如果其中一个线程遇到返回NULL的malloc(),程序应该怎么做?到目前为止我已经提出了可能性:
第一种选择显然是最简单的,但似乎非常错误。第二个也似乎错了,因为我不确切知道会发生什么。除了两个问题之外,第三个选项似乎很诱人:首先,在正常情况下,所有线程都不需要连接回主线程,其次,为了完成线程执行,大多数剩余线程将不得不调用malloc(无论如何,再次。
我该怎么办?
答案 0 :(得分:4)
选项2没有任何问题。您不必假设 - exit()
退出流程,这意味着所有线程都被拆除,一切都被清理干净。
不要忘记尝试记录发生失败分配的位置。
答案 1 :(得分:4)
这是space / rad hard systems一般禁止动态内存分配的原因之一。当malloc()
失败时,极难“治愈”失败。你有一些选择:
malloc()
(根本不需要或像往常一样)。您可以wrap malloc()对失败进行额外的工作,例如通知其他内容。这在使用看门狗之类的东西时很有用。您也可以使用full blown garbage collector,但我不推荐它。更好地识别和修复泄漏。malloc()
,它不会过度使用它。如果您已经广泛地分析了堆的使用情况(使用Valgrind的 massif 等工具),则可以合理地调整池的大小。但是,如果失败不是一种选择,大多数建议归结为不信任/使用系统malloc()
。
在您的情况下,我认为您可以做的最好的事情是确保在malloc()
失败的情况下通知监视器,以便您的进程(或整个系统)可以重新开始。在死锁时你不希望它看起来“活着并且正在运行”。这可以像取消链接文件一样简单。
写出非常详细的日志。发生故障的文件/行/功能是什么?
如果malloc()
在尝试获得几KB时失败,则表明您的流程无论如何都无法可靠地继续运行。如果它没有抓住几百MB,你可能能够恢复并继续前进。通过该令牌,您采取的任何操作都应该基于您尝试获取多少内存,以及分配更小尺寸的调用是否仍然成功。
你永远不想做的一件事就是操作NULL指针并让它崩溃。它只是草率,没有提供有用的记录错误的地方,并给人的印象是你的软件质量低/不稳定。
答案 2 :(得分:2)
还有第四种选择:释放一些内存(缓存总是很好的候选者)并再试一次。
如果你负担不起,我会选择选项2(显然记录或打印某种错误信息)......清理的唯一问题是有序关闭打开的网络连接,所以客户端知道另一方的应用程序正在关闭而不是发现意外的连接问题。
答案 3 :(得分:0)
我认为取决于您的架构。
malloc()
失败是否意味着该线程无法继续,或者整个过程是否在这种情况下陷入困境?
通常,当内存非常紧张时(即微处理器环境),最好避免所有动态内存分配以避免这样的问题。
答案 4 :(得分:0)
从个人经验来看,我可以说,malloc失败的频率经常被高估。例如,在Linux中,通常的“解决方案”是2的变体,并且您没有得到malloc故障。一个过程突然死了。在较大的系统上,一旦交换使应用程序无响应,应用程序就会死亡,因为用户或监视器会将其杀死。
这使得清理变得更加困难,并且也很难提出一般解决方案。
答案 5 :(得分:0)
这是在操作系统上运行吗? pthreads的使用表明了这一点。你知道即使malloc()会返回NULL吗?在某些系统(例如Linux)上,故障将发生在malloc()中,并且将由操作系统处理(通过终止进程),而不会返回malloc()。
我建议您在初始化应用程序时分配一个内存池,然后从中分配,而不是在初始化后使用malloc()。这将使您可以控制内存分配算法以及内存耗尽时的行为。如果池中的内存不足,则在应用程序有机会启动无法完成的任何操作之前,初始化时会出现单点故障。
在实时和嵌入式系统中,通常使用'fixed-block memory allocator'。如果您的操作系统不提供服务,可以通过预先分配内存块并将其指针放在队列上来实现。要分配一个块,您需要从队列中获取一个指针,并释放它,然后将其放回队列中。当队列为空时,内存耗尽,您可以阻止并处理错误,或者阻塞并等待,直到另一个线程返回一些内存。您可能希望创建具有不同大小的块的多个池,或者甚至为特定目的创建池,其中块具有为此目的所需的精确大小。