【问题标题】:malloc error in Linux, the same code on Mac OS X runs wellLinux 中的 malloc 错误,Mac OS X 上的相同代码运行良好
【发布时间】:2013-11-15 19:52:02
【问题描述】:

当我在 Mac OS X 中运行我的代码时,它运行良好。但是在 Linux (Ubuntu) 上,我得到以下输出:

pmichna@pawel-mac:~/alchemists$ ./alchemists 10
Creating 10 alchemists...
TESTING_ERROR #0
alchemists: malloc.c:2369: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed.
make: *** [run] Aborted (core dumped)

怎么了? 我的代码:

pmichna@pawel-mac:~/alchemists$ cat alchemists.c

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define NUM_APPRENTICES 2

pthread_mutex_t *ingredients_mut;
pthread_mutex_t *pots_mut;
pthread_cond_t *ingredients_cond;
pthread_cond_t *pots_cond;
int *ingredients_count;
long num_of_pots;
long num_of_alchemists;
int *pots_usage;
// if the pot was used by the alchemist on the right: 1
// if on the left: -1
// if not yet used: 0

void lock_ingredients(long ingredients_index, long tid)
{
    printf("Alchemist #%ld trying to lock ingredients from bowl #%ld...\n", tid, ingredients_index);
    pthread_mutex_lock(&ingredients_mut[ingredients_index]);
    if(ingredients_count[ingredients_index]>1)
    {
        ingredients_count[ingredients_index]--;
        printf("Alchemist #%ld took an ingredient from bowl #%ld!\n", tid, ingredients_index);
    }
    else {
        printf("Alchemist #%ld found the bowl #%ld empty. Waiting for the filling apprentice...\n", tid, ingredients_index);
        pthread_cond_wait(&ingredients_cond[ingredients_index], &ingredients_mut[ingredients_index]);
    }
}

void lock_pot(long pot_index, long tid, int expected_pot_value)
{
    printf("Alchemist #%ld trying to lock pot #%ld...\n", tid, pot_index);
    pthread_mutex_lock(&pots_mut[pot_index]);
    if(pots_usage[pot_index] == 0 || pots_usage[pot_index] == expected_pot_value)
    {
        pots_usage[pot_index] = expected_pot_value;
        printf("Alchemist #%ld locked pot #%ld!\n", tid, pot_index);
    } else {
        printf("Alchemist #%ld found the pot #%ld dirty. :(\n", tid, pot_index);
        pthread_cond_wait(&pots_cond[pot_index], &pots_mut[pot_index]);
        printf("Alchemist #%ld now can use the cleaned pot #%ld!\n", tid, pot_index);
    }
}

void finish_production(int *gold_produced, long tid, long ingredients_index, long pot_index)
{
    (*gold_produced)++;
    printf("Alchemist #%ld produced gold!. Current status: %d\n", tid, *gold_produced);

    pthread_mutex_unlock(&ingredients_mut[ingredients_index]);
    printf("Alchemist #%ld released the bowl #%ld.\n", tid, ingredients_index);
    pthread_mutex_unlock(&pots_mut[pot_index]);
    printf("Alchemist #%ld released the pot #%ld.\n", tid, pot_index);
}


void alchemist_even_work(long tid, int *gold_produced) // has pot on the left; bowl on the right
{
    long pot_index = tid/2 - 1;
    long ingredients_index = tid/2;
    int expected_pot_value = 1;

    lock_ingredients(ingredients_index, tid);
    lock_pot(pot_index, tid, expected_pot_value);
    finish_production(gold_produced, tid, ingredients_index, pot_index);
}

void alchemist_odd_work(long tid, int *gold_produced) //has pot on the right; bowl on the left
{
    long pot_index = tid/2;
    long ingredients_index = tid/2;
    int expected_pot_value = -1;

    lock_ingredients(ingredients_index, tid);
    lock_pot(pot_index, tid, expected_pot_value);
    finish_production(gold_produced, tid, ingredients_index, pot_index);
}

void alchemist_first_work(long tid, int *gold_produced) // has pot on the left; bowl on the right
{
    long pot_index = num_of_alchemists/2 - 1;
    long ingredients_index = 0;
    int expected_pot_value = 1;

    lock_ingredients(ingredients_index, tid);
    lock_pot(pot_index, tid, expected_pot_value);
    finish_production(gold_produced, tid, ingredients_index, pot_index);
}

void *alchemist_work(void *id)
{
    long tid = (long)id;
    int sleep_time, gold_produced = 0;
    printf("Alchemist #%ld started\n", tid);

    while(gold_produced < 10)
    {
        //thinking
        srand((int)tid);
        sleep_time = rand()%5+1;
        printf("Alchemist #%ld going to think %d seconds...\n", tid, sleep_time);
        sleep(sleep_time);
        printf("Alchemist #%ld is going to try to make gold!\n", tid);

        if(tid == 0)
        {
            alchemist_first_work(tid, &gold_produced);
        } else if(tid%2 == 0)
        {
            alchemist_even_work(tid, &gold_produced);
        } else {
            alchemist_odd_work(tid, &gold_produced);
        }
    }
    printf("Alchemist #%ld going to lunch.\n", tid);
    pthread_exit(NULL);
}

void *apprentice_work(void *id)
{
    long tid = (long)id;
    long i;
    int sleep_time;
    switch (tid) {
        case 0:
            printf("Cleaning apprentice started.\n");
            break;

        case 1:
            printf("Ingredients apprentice started.\n");
            break;
    }
    srand((int)tid);

    while (1)
    {
        sleep_time = rand()%5+1;
        switch (tid) {
            case 0: // cleaner of pots
                printf("Cleaning apprentice going to sleep %d seconds...\n", sleep_time);
                sleep(sleep_time);
                printf("Cleaning apprentice is now going to clean all the pots\n");
                for(i = 0; i < num_of_pots; i++)
                {
                    pthread_mutex_lock(&pots_mut[i]);
                    printf("Apprentice cleaning pot #%ld\n", i);
                    pots_usage[i] = 0;
                    pthread_cond_signal(&pots_cond[i]);
                    pthread_mutex_unlock(&pots_mut[i]);
                }
                break;

            case 1: // ingredients
                printf("Ingredients apprentice going to sleep %d seconds...\n", sleep_time);
                sleep(sleep_time);
                printf("Ingredients apprentice going to fill all the pots...\n");
                for(i = 0; i < num_of_pots; i++)
                {
                    pthread_mutex_lock(&ingredients_mut[i]);
                    printf("Apprentice filling bowl #%ld\n", i);
                    ingredients_count[i] = 5;
                    pthread_cond_broadcast(&ingredients_cond[i]);
                    pthread_mutex_unlock(&ingredients_mut[i]);
                }
                break;
        }
    }
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    long i;
    num_of_alchemists = atol(argv[1]);;
    if(num_of_alchemists%2 == 1)
    {
            fprintf(stderr, "Only even number of alchemists possible!\n");
            exit(1);
    }
    num_of_pots = num_of_alchemists/2; // this variable used alse as number of bowls with ingredients
    pthread_t threads_alchemists[num_of_alchemists];
    pthread_t threads_apprentices[NUM_APPRENTICES];
    pthread_attr_t attr;

    ingredients_mut = malloc(num_of_pots*sizeof(pthread_mutex_t));
    pots_mut = malloc(num_of_pots*sizeof(pthread_mutex_t));
    ingredients_cond = malloc(num_of_pots*sizeof(pthread_cond_t));
    ingredients_count = malloc(num_of_pots*sizeof(int));
    pots_usage = malloc(num_of_pots*sizeof(int));
    pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));

    for(i = 0; i < num_of_pots; i++)
    {
        pthread_mutex_init(&ingredients_mut[i], NULL);
        pthread_mutex_init(&pots_mut[i], NULL);
        pthread_cond_init(&ingredients_cond[i], NULL);
        pthread_cond_init(&pots_cond[i], NULL);
        ingredients_count[i] = 5;
        pots_usage[i] = 0;
    }

    /* For portability, explicitly create threads in a joinable state */
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    printf("Creating %ld alchemists...\n", num_of_alchemists);
    for(i = 0; i < num_of_alchemists; i++)
    {
    printf("TESTING_ERROR #%ld\n", i);
        pthread_create(&threads_alchemists[i], &attr, alchemist_work, (void *)i);
    }
    for(i = 0; i < NUM_APPRENTICES; i++)
    {
        pthread_create(&threads_apprentices[i], &attr, apprentice_work, (void *)i);
    }

    /* Wait for all threads to complete */
    for (i = 0; i < num_of_alchemists; i++)
    {
        pthread_join(threads_alchemists[i], NULL);
    }
    printf ("Main(): Waited and joined with %ld alchemist threads.\n", num_of_alchemists);

    // Clean up
    pthread_attr_destroy(&attr);
    for(i = 0; i < num_of_pots; i++)
    {
        pthread_mutex_destroy(&ingredients_mut[i]);
        pthread_mutex_destroy(&pots_mut[i]);
        pthread_cond_destroy(&ingredients_cond[i]);
        pthread_cond_destroy(&pots_cond[i]);
    }
    free(ingredients_mut);
    free(ingredients_count);
    free(ingredients_cond);
    free(pots_mut);
    free(pots_usage);
    free(pots_cond);
    return 0;
}

更新:

所以使用了valgrind,程序运行起来,不时吐出错误。我注意到,它们主要涉及条件变量。一个刚开始出现的例子:

pmichna@pawel-mac:~/alchemists$ valgrind --tool=memcheck ./alchemists 4
==3552== Memcheck, a memory error detector
==3552== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3552== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3552== Command: ./alchemists 4
==3552== 
==3552== Invalid write of size 4
==3552==    at 0x4E3FA32: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:33)
==3552==    by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552==  Address 0x541a2f8 is 8 bytes after a block of size 80 alloc'd
==3552==    at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552==    by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552== 
==3552== Invalid write of size 8
==3552==    at 0x4E3FA41: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:40)
==3552==    by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552==  Address 0x541a2f0 is 0 bytes after a block of size 80 alloc'd
==3552==    at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552==    by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552== 
==3552== Invalid write of size 4
==3552==    at 0x4E3FA57: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:42)
==3552==    by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552==  Address 0x541a2fc is 12 bytes after a block of size 80 alloc'd
==3552==    at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552==    by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552== 
Creating 4 alchemists...
TESTING_ERROR #0
TESTING_ERROR #1
Alchemist #0 started
TESTING_ERROR #2
TESTING_ERROR #3
Alchemist #1 started
Alchemist #1 going to think 4 seconds...
Alchemist #2 started
Alchemist #2 going to think 1 seconds...
Alchemist #3 started

这是否意味着我没有正确使用pthread_cond_init 函数?我该如何解决这个问题?

【问题讨论】:

  • 那是很多代码。你试过用 Valgrind 运行它吗?
  • @DietrichEpp 不,我没有 - 我听说过它,但我没有使用它。但是,您可以看到它失败的地方。它设法写入“测试错误#0”。这是在创建第一个线程之前循环中的main 函数。
  • 好吧,然后尝试使用 Valgrind 运行它。
  • 使用调试器逐步完成。
  • @DietrichEpp 我添加了一些 Valgrind 输出。不过,这对我帮助不大。

标签: c linux multithreading macos posix


【解决方案1】:

您在 pots_cond 的元素上调用 pthread_cond_init

pthread_cond_init(&pots_cond[i], NULL);

但是分配pots_cond时使用的大小是pthread_mutex_t的大小(而不是pthread_cond_t):

pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));

在我的 OS X 系统上,sizeof(pthread_cond_t) 是 48,但 sizeof(pthread_mutex_t) 是 64,所以这不会导致问题(因为您实际上分配的内存超出了您的需要。)

但是,当我启动 Ubuntu Linux 虚拟机进行检查时,发现 sizeof(pthread_mutex_t) 只有 40,在这种情况下,您的代码分配的内存少于您需要的内存,您将超出 @987654331 @。

【讨论】:

  • 我就知道会是这样的问题...非常感谢!
  • 这就是 Valgrind 很棒的原因。 :-)
【解决方案2】:
pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));  

您分配的大小为pthread_mutex_t。但是你想要一个pthread_cond_t。总是在这个表单上做 malloc:

  pots_cond = malloc(num_of_pots*sizeof *pots_cond);

这里的评论也是骗人的:

/* Wait for all threads to complete */ 
for (i = 0; i < num_of_alchemists; i++)
{
     pthread_join(threads_alchemists[i], NULL);
}

你只等待threads_alchemists,而不是你所有的&amp;threads_apprentices。当所有的炼金术士都完成后,您将继续 free() 学徒线程正在使用的变量。

【讨论】:

    【解决方案3】:

    将整数转换为void*,然后作为参数返回给线程函数从根本上说是错误的。如果您使用的机器上longvoid* 的宽度不同,那么您最终可能会在单词的上半部分出现错误的内容。

    改为使用long[something] 的表,并将此类表条目的地址传递给每个线程。

    【讨论】:

    • 这是个糟糕的建议,因为它会使代码更加复杂,而您可以使用 intptr_t 而不是 long
    • @DietrichEpp 我们显然不同意那个。如果您违反接口并且使用无用的强制转换,代码就会变得复杂。称这个“坏建议”肯定有点强烈,我可以接受你的。养成通过void* 无偿转换整数的习惯就是一个坏习惯,我不认为在像 SO 这样的网站上这个好建议。
    • 任何时候你分配线程之间共享的内存,你必须考虑该内存的生命周期,以及相对于使用该内存的线程的生命周期处理该内存的责任。而如果您使用演员表,则根本不需要共享内存。养成无偿创建新的共享对象并在线程之间传递它们的习惯就是这样,一个坏习惯,我不认为在像 SO 这样的网站上这个好建议。
    • 或者让我改写一下:任何时候你投射一些东西都有出错的风险,每次你使用共享内存都有出错的风险。我认为使用共享内存的风险远高于强制转换的风险,所以在这种情况下,我更喜欢强制转换。这与习惯无关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    • 2013-11-25
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    相关资源
    最近更新 更多