c - Linux 中的 malloc 错误,相同的代码在 Mac OS X 上运行良好

标签 c linux multithreading macos posix

当我在 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函数?我该如何解决这个问题?

最佳答案

您正在对 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 VM 进行检查时,结果是 sizeof(pthread_mutex_t) 只有 40,在这种情况下,您的代码分配的内存少于你需要,你会超过 pots_cond

关于c - Linux 中的 malloc 错误,相同的代码在 Mac OS X 上运行良好,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20009448/

相关文章:

c++ - 清理 RAM(也许是 win32 api)?

c - 重新附加到 Linux 中的进程

python - 在我们连接之前,浏览器似乎已经退出。输出为 : mkdir: cannot create directory

linux - 在具有 x64 操作系统 (redhat 5.6) 的 x64 CPU (Xeon 7650) 上运行的 X64 应用程序的 2GB 障碍 - 为什么 + 要检查的内容

android - libGDX 的 Looper 错误

c - main函数在返回0后不会退出,shmctl不会删除段

c - 为什么 Linux 内核模块符号不能正确地全局导出?

multithreading - 在C++ 11中从外部终止线程

python - md5多线程暴力破解

c - 此代码有什么问题?每次显示错误的输出