繁体   English   中英

多线程C应用程序应如何处理失败的malloc()?

[英]How should a multi-threaded C application handle a failed malloc()?

我正在处理的应用程序的一部分是一个简单的基于pthread的服务器,它通过TCP / IP套接字进行通信。 我在C中编写它是因为它将在内存受限的环境中运行。 我的问题是:如果其中一个线程遇到返回NULL的malloc(),程序应该怎么做? 到目前为止我想出的可能性:

  1. 没有特别处理。 让malloc()返回NULL并让它被解除引用,以便整个事件发生段错误。
  2. 通过调用abort()或exit(-1)立即退出失败的malloc()。 假设环境将清理一切。
  3. 跳出主事件循环并尝试pthread_join()所有线程,然后关闭。

第一种选择显然是最简单的,但似乎非常错误。 第二个也似乎错了,因为我不确切知道会发生什么。 除了两个问题之外,第三个选项似乎很诱人:首先,在正常情况下,所有线程都不需要连接回主线程,其次,为了完成线程执行,大多数剩余线程将不得不调用malloc( )无论如何。

我该怎么办?

选项2没有任何问题。您不必假设 - exit()退出进程,这意味着所有线程都被拆除,一切都被清理干净。

不要忘记尝试记录发生失败分配的位置。

这是空间/ rad硬系统通常禁止动态内存分配的原因之一。 malloc()失败时,它很难“治愈”失败。 你有一些选择:

  • 您不需要使用内置的libc malloc() (根本不需要或像往常一样)。 您可以将malloc()包装为对故障执行额外的工作,例如通知其他内容。 这在使用看门狗之类的东西时很有用。 您也可以使用完整的垃圾收集器 ,但我不推荐它。 更好地识别和修复泄漏。
  • 根据存储和复杂性,不经常访问的已分配块可以映射到磁盘。 但在这里,通常情况下,您只需要节省几KB的物理内存。
  • 您可以使用静态内存池和您自己的malloc() ,它不会过度使用它。 如果您已经广泛地分析了堆的使用情况(使用Valgrind的或类似工具),则可以合理地调整池的大小。

但是,如果失败不是一种选择,大多数建议归结为不信任/使用系统malloc()

在您的情况下,我认为您可以做的最好的事情是确保malloc()失败的情况下通知监视程序,以便可以重新启动您的进程(或整个系统)。 在死锁时你不希望它看起来“活着并且正在运行”。 这可能就像取消链接文件一样简单。

写非常详细的日志。 失败发生在什么文件/行/功能上?

如果malloc()在尝试获得几KB时失败,那么这表明您的流程无论如何都无法可靠地继续运行。 如果它没有抓住几百MB,你可能能够恢复并继续前进。 通过该令牌,您采取的任何操作都应该基于您尝试获取多少内存,以及分配更小尺寸的调用是否仍然成功。

你永远不想做的一件事就是操作NULL指针并让它崩溃。 它只是草率,没有提供有用的记录错误的地方,并给人的印象是你的软件质量低/不稳定。

还有第四个选择:释放一些内存(缓存总是很好的候选者)并再试一次。

如果你负担不起,我会选择选项2(显然记录或打印某种错误信息)...清理的唯一问题是以有序的方式关闭打开的网络连接,所以客户知道另一方面的应用程序正在关闭,而不是发现意外的连接问题。

我认为取决于你的架构。

malloc()是否失败意味着只有那个线程不能继续,或者在这种情况下是否整个过程都被堵塞了?

通常,当内存非常紧张时(即微处理器环境),最好避免所有动态内存分配以避免这样的问题。

从个人经验来看,我可以看出malloc故障的频率经常被高估。 例如,在Linux中,通常的“解决方案”是2的变体,并且您没有得到malloc故障。 一个过程突然死了。 在较大的系统上,一旦交换使应用程序无响应,应用程序就会死亡,因为用户或监视器会将其杀死。

这使得清理变得更加困难,并且也很难提出一般解决方案。

这是在OS上运行吗? pthreads的使用表明了这一点。 你知道即使malloc()会返回NULL吗? 在某些系统(例如Linux)上,故障将发生在malloc()中,并且将由操作系统处理(通过终止进程)而不返回malloc()。

我建议您在初始化应用程序时分配一个内存池,然后从中分配,而不是在初始化后使用malloc()。 这将使您可以控制内存分配算法以及内存耗尽时的行为。 如果池中的内存不足,则在应用程序有机会启动无法完成的任何操作之前,初始化时会出现单点故障。

在实时和嵌入式系统中,通常使用“固定块存储器分配器” 如果您的操作系统不提供服务,可以通过预先分配内存块并将其指针放在队列上来实现。 要分配一个块,您需要从队列中获取一个指针,并释放它,然后将其放回队列中。 当队列为空时,内存耗尽,您可以阻止并处理错误,或者阻塞并等待,直到另一个线程返回一些内存。 您可能希望创建具有不同大小的块的多个池,或甚至为特定目的创建池,其中块具有为此目的所需的精确大小。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM