You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加printf语句后Realloc触发“invalid next size”错误排查求助

添加printf语句后Realloc触发“invalid next size”错误排查求助

嘿,我帮你仔细过了一遍代码,发现了几个很可能导致堆损坏的问题,尤其是第一个错误几乎肯定就是你遇到的core dump的元凶!

1. 初始化task_queue时的memset越界(最关键问题)

看你sdm_threadpool_create函数里这段代码:

pool->task_queue = malloc(sizeof(sdm_threadpool_task_t) * queue_capacity);
if (pool->task_queue == NULL) {
  fprintf(stderr, "Memory allocation problem. Quiting\n");
  exit(1);
}
memset(pool->task_queue, 0, pool->num_threads * sizeof(pool->task_queue[0]));

你犯了一个低级但致命的错误:malloc是按queue_capacity分配的空间,但memset却用pool->num_threads来计算要初始化的字节数!

举个例子:如果DEFAULT_QUEUE_LENGTH(初始队列长度)比NUM_THREADS小,memset会直接写超过malloc分配的内存范围,把堆的元数据彻底破坏。堆结构被破坏后,后续的realloc调用就会检测到异常,抛出realloc(): invalid next size的错误。

那为什么注释掉printf就没事?其实不是真的没事,只是堆损坏的症状没被触发而已——printf会放慢sdm_threadpool_add的执行速度,改变线程处理任务的时序,刚好让realloc在堆损坏后立刻被调用,从而暴露问题;没有printf的时候,时序刚好避开了触发检测的时机,但堆其实已经被破坏了,只是没立刻崩溃。

修正方法:把memset的计数改成queue_capacity:

memset(pool->task_queue, 0, queue_capacity * sizeof(pool->task_queue[0]));

2. 队列扩容的逻辑错误

再看sdm_threadpool_add里的扩容循环:

while (pool->waiting_in_queue >= pool->queue_capacity) {
  pool->queue_capacity *= 2;
  size_t new_size = sizeof(sdm_threadpool_task_t) * pool->queue_capacity;
  printf("Extending queue to %zu bytes\n", new_size);
  sdm_threadpool_task_t *new_task_queue = realloc(pool->task_queue, new_size);
  if (new_task_queue == NULL) {
    fprintf(stderr, "Memory allocation problem. Quiting\n");
    exit(1);
  }
  pool->task_queue = new_task_queue;
}

你用waiting_in_queue >= pool->queue_capacity作为扩容条件完全错误。waiting_in_queue是还没被线程取走的任务数,而queue_capacity是队列的总容量。实际上,你需要检查的是已添加的任务总数(也就是pool->queue_length)是否超过了队列容量——因为你是用线性数组实现队列,所有已添加的任务都存在数组里,不管线程有没有取走。

如果线程处理任务的速度很快,waiting_in_queue可能永远达不到queue_capacity,但此时queue_length已经超过了队列实际容量,你往task_queue[queue_length]写数据时会越界,再次破坏堆结构。

修正方法:把扩容条件改成检查queue_length,而且用if就够了(因为每次扩容翻倍,一次就足够容纳当前所有任务):

if (pool->queue_length >= pool->queue_capacity) {
  pool->queue_capacity *= 2;
  size_t new_size = sizeof(sdm_threadpool_task_t) * pool->queue_capacity;
  printf("Extending queue to %zu bytes\n", new_size);
  sdm_threadpool_task_t *new_task_queue = realloc(pool->task_queue, new_size);
  if (new_task_queue == NULL) {
    fprintf(stderr, "Memory allocation problem. Quiting\n");
    exit(1);
  }
  pool->task_queue = new_task_queue;
}

3. 额外的潜在问题(非当前崩溃原因,但需注意)

最后提两个不导致当前崩溃,但有隐患的点:

  • 线性队列的空间浪费:线程处理任务时只递增next_in_queue,从不复用前面已处理完的任务空间,会导致队列数组越变越大,即使大部分空间已经没用。后续可以改成环形队列优化。
  • sdm_threadpool_join的轮询不安全:用usleep轮询waiting_in_queue是否为0,不仅效率低,而且线程不安全——你在没有持有锁的情况下读取waiting_in_queue,可能读到过期的值。正确做法是用条件变量等待:在sdm_threadpool_join里持有锁,循环等待waiting_in_queue == 0,用pthread_cond_wait挂起线程,而非主动轮询。

把前两个错误修复后,你再打开printf应该就不会出现core dump了。堆损坏问题很多时候看起来是随机的,但本质都是越界写内存破坏了堆结构,只要找到越界的地方就能解决。


备注:内容来源于stack exchange,提问作者smolloy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 02:58:04