Malloc分段错误

gui*_*mtr 11 c stack-overflow malloc buffer-overflow segmentation-fault

以下是发生分段错误的代码段(不会调用perror):

job = malloc(sizeof(task_t));
if(job == NULL)
    perror("malloc");
Run Code Online (Sandbox Code Playgroud)

更确切地说,gdb说segfault__int_malloc调用内部发生,这是一个子例程调用malloc.

由于malloc函数与其他线程并行调用,最初我认为它可能是问题所在.我使用的是glibc 2.19版.

数据结构:

typedef struct rv_thread thread_wrapper_t;

typedef struct future
{
  pthread_cond_t wait;
  pthread_mutex_t mutex;
  long completed;
} future_t;

typedef struct task
{
  future_t * f;
  void * data;
  void *
  (*fun)(thread_wrapper_t *, void *);
} task_t;

typedef struct
{
  queue_t * queue;
} pool_worker_t;

typedef struct
{
  task_t * t;
} sfuture_t;

struct rv_thread
{
  pool_worker_t * pool;
};
Run Code Online (Sandbox Code Playgroud)

现在未来的实施:

future_t *
create_future()
{
  future_t * new_f = malloc(sizeof(future_t));
  if(new_f == NULL)
    perror("malloc");
  new_f->completed = 0;
  pthread_mutex_init(&(new_f->mutex), NULL);
  pthread_cond_init(&(new_f->wait), NULL);
  return new_f;
}

int
wait_future(future_t * f)
{
  pthread_mutex_lock(&(f->mutex));
  while (!f->completed)
    {
      pthread_cond_wait(&(f->wait),&(f->mutex));
    }
  pthread_mutex_unlock(&(f->mutex));
  return 0;
}

void
complete(future_t * f)
{
  pthread_mutex_lock(&(f->mutex));
  f->completed = 1;
  pthread_mutex_unlock(&(f->mutex));
  pthread_cond_broadcast(&(f->wait));
}
Run Code Online (Sandbox Code Playgroud)

线程池本身:

pool_worker_t *
create_work_pool(int threads)
{
  pool_worker_t * new_p = malloc(sizeof(pool_worker_t));
  if(new_p == NULL)
    perror("malloc");
  threads = 1;
  new_p->queue = create_queue();
  int i;
  for (i = 0; i < threads; i++){
    thread_wrapper_t * w = malloc(sizeof(thread_wrapper_t));
    if(w == NULL)
      perror("malloc");
    w->pool = new_p;
    pthread_t n;
    pthread_create(&n, NULL, work, w);
  }
  return new_p;
}

task_t *
try_get_new_task(thread_wrapper_t * thr)
{
  task_t * t = NULL;
  try_dequeue(thr->pool->queue, t);
  return t;
}

void
submit_job(pool_worker_t * p, task_t * t)
{
  enqueue(p->queue, t);
}

void *
work(void * data)
{
  thread_wrapper_t * thr = (thread_wrapper_t *) data;
  while (1){
    task_t * t = NULL;
    while ((t = (task_t *) try_get_new_task(thr)) == NULL);
    future_t * f = t->f;
    (*(t->fun))(thr,t->data);
    complete(f);
  }
  pthread_exit(NULL);
}
Run Code Online (Sandbox Code Playgroud)

最后是task.c:

pool_worker_t *
create_tpool()
{
  return (create_work_pool(8));
}

sfuture_t *
async(pool_worker_t * p, thread_wrapper_t * thr, void *
(*fun)(thread_wrapper_t *, void *), void * data)
{
  task_t * job = NULL;
  job = malloc(sizeof(task_t));
  if(job == NULL)
    perror("malloc");
  job->data = data;
  job->fun = fun;
  job->f = create_future();
  submit_job(p, job);
  sfuture_t * new_t = malloc(sizeof(sfuture_t));
  if(new_t == NULL)
    perror("malloc");
  new_t->t = job;
  return (new_t);
}

void
mywait(thread_wrapper_t * thr, sfuture_t * sf)
{
  if (sf == NULL)
    return;
  if (thr != NULL)
    {
      while (!sf->t->f->completed)
        {
          task_t * t_n = try_get_new_task(thr);
          if (t_n != NULL)
            {
          future_t * f = t_n->f;
          (*(t_n->fun))(thr,t_n->data);
          complete(f);
            }
        }
      return;
    }
  wait_future(sf->t->f);
  return ;
}
Run Code Online (Sandbox Code Playgroud)

队列是lfds无锁队列.

#define enqueue(q,t) {                                 \
    if(!lfds611_queue_enqueue(q->lq, t))             \
      {                                               \
        lfds611_queue_guaranteed_enqueue(q->lq, t);  \
      }                                               \
  }

#define try_dequeue(q,t) {                            \
    lfds611_queue_dequeue(q->lq, &t);               \
  }
Run Code Online (Sandbox Code Playgroud)

只要对异步的调用次数非常高,就会出现问题.

Valgrind输出:

Process terminating with default action of signal 11 (SIGSEGV)
==12022==  Bad permissions for mapped region at address 0x5AF9FF8
==12022==    at 0x4C28737: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
Run Code Online (Sandbox Code Playgroud)

Jek*_*yll 13

在malloc中触发的SIGSEGV(分段错误)通常是由堆损坏引起的.堆损坏不会导致分段错误,因此只有当malloc尝试访问时才会看到.问题是创建堆损坏的代码可能在任何位置甚至远离调用malloc的位置.它通常是malloc中的下一个块指针,它被堆损坏更改为无效地址,因此当您调用malloc时,无效指针会被取消引用并且您会遇到分段错误.

我认为您可以尝试从程序的其余部分中隔离代码的一部分,以降低错误的可见性.

此外,我发现你永远不会释放内存,可能会有内存泄漏.

为了检查内存泄漏,您可以运行top命令top -b -n 1并检查:

RPRVT - resident private address space size
RSHRD - resident shared address space size
RSIZE - resident memory size
VPRVT - private address space size
VSIZE - total memory size
Run Code Online (Sandbox Code Playgroud)


gui*_*mtr 12

我已经弄清楚问题是什么:堆栈溢出.

首先,让我解释为什么在malloc内部发生堆栈溢出(这可能就是你读这个的原因).当我的程序运行时,每次开始执行(递归)另一个任务(因为我编写它的方式)时,堆栈大小不断增加.但是对于每一个这样的时间,我不得不使用malloc分配一个新任务.但是,malloc进行其他子例程调用,这使得堆栈的大小增加甚至超过执行另一个任务的简单调用.所以,发生的事情是,即使没有malloc,我也会得到堆栈溢出.但是,因为我有malloc,所以堆栈溢出的那一刻是在malloc中,在它溢出之前再进行另一次递归调用.下面的插图显示了发生的事情:

初始堆栈状态:

-------------------------
| recursive call n - 3  |
-------------------------
| recursive call n - 2  |
-------------------------
| recursive call n - 1  |
-------------------------
|        garbage        |
-------------------------
|        garbage        | <- If the stack passes this point, the stack overflows.
-------------------------
Run Code Online (Sandbox Code Playgroud)

在malloc调用期间堆栈:

-------------------------
| recursive call n - 3  |
-------------------------
| recursive call n - 2  |
-------------------------
| recursive call n - 1  |
-------------------------
|        malloc         |
-------------------------
|     __int_malloc      | <- If the stack passes this point, the stack overflows.
-------------------------
Run Code Online (Sandbox Code Playgroud)

然后堆栈又缩小了,我的代码进入了一个新的递归调用:

-------------------------
| recursive call n - 3  |
-------------------------
| recursive call n - 2  |
-------------------------
| recursive call n - 1  |
-------------------------
| recursive call n      |
-------------------------
|        garbage        | <- If the stack passes this point, the stack overflows.
-------------------------
Run Code Online (Sandbox Code Playgroud)

然后,它在这个新的递归调用中再次调用malloc.但是,这次它溢出了:

-------------------------
| recursive call n - 3  |
-------------------------
| recursive call n - 2  |
-------------------------
| recursive call n - 1  |
-------------------------
| recursive call n      |
-------------------------
|        malloc         | <- If the stack passes this point, the stack overflows.
-------------------------
|     __int_malloc      | <- This is when the stack overflow occurs.
-------------------------
Run Code Online (Sandbox Code Playgroud)

[其余的答案更侧重于我为什么在我的代码中遇到这个问题.]

通常,当递归地计算Fibonacci(例如,具有特定数量n)时,堆栈大小随该数量线性增长.但是,在这种情况下,我正在创建任务,使用队列来存储它们,并将(fib)任务出列以执行.如果你在纸上绘制这个,你会发现任务的数量随着n而呈指数增长,而不是线性增长(还要注意,如果我在创建任务时使用了堆栈来存储任务,那么任务分配为因为堆栈大小只会随着n线性增长.所以会发生的事情是堆栈随n呈指数增长,导致堆栈溢出......现在出现了为什么在malloc调用中发生这种溢出的部分.所以基本上,as我在上面解释过,堆栈溢出发生在malloc调用中,因为它是堆栈最大的地方.发生的事情是堆栈几乎爆炸,并且由于malloc调用其中的函数,堆栈增长的不仅仅是调用mywait和FIB.

谢谢你们!如果这不是你的帮助,我将无法弄明白!