内存泄漏和指针

3
我已经实现了一个程序,使用线程将数组元素相加。每个线程将数组的一部分元素相加,并将结果存入数组中。最后,另一个线程将这些结果相加。
这个程序似乎可以工作,但我发现有很多内存泄漏问题,我无法解决。我正在使用valgrind来检测这些内存泄漏。我认为我的指针知识缺少一些关键特性。
我也尝试在线程返回之前释放结构体,但却导致了核心转储。我已经尝试在分配内存的地方释放内存,但valgrind报告的分配数量大于释放数量。
现在,看一下代码:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define MAX_RAND 100
/**
 * @brief global variables shared by threads
 *      array -> the array of values
 *      size-> the size of the array
 *      numberOfThreads -> the number of threads used to compute the sum
 * 
 */
int *array;
int size;
int numberOfThreads = 3;

/**
 * @brief each thread works on  disjoint segments of an array(i.e. two or more threads shall never work on the same segment). This
 * encapsulates the start and end indexes used by a thread.
 * 
 */
typedef struct
{
    int startIndex;
    int endIndex;
} wrapper;

/**
 * @brief this is used by the extra thread and contains the array of sums computed by the previous threads, along the size of the results
 * array
 * 
 */
typedef struct
{
    int *array;
    int size;
} resultsWrapper;

/**
 * @brief this computes the sum of a disjoint segment of the shared array
 * 
 * @param args 
 * @return void* 
 */
void *sum_runner(void *args)
{
    /**
     * @brief cast the argument and retrieve the indexes
     * 
     */
    wrapper *data = (wrapper *)args;
    int currentIndex = data->startIndex;
    int endIndex = data->endIndex;
    int *sum = (int *)malloc(sizeof(int *));
    *sum = 0;

    /**
     * @brief iterate that segment and compute the sum
     * 
     */
    while (currentIndex < endIndex)
    {
        *sum += array[currentIndex];
        currentIndex++;
    }

    /**
     * @brief return the sum
     * 
     */
    pthread_exit((void *)sum);
}

/**
 * @brief this is used by the an extra thread to sum up the elements computed by the previouse threads
 * 
 * @param args 
 * @return void* 
 */
void *results_sum_runner(void *args)
{
    /**
     * @brief cast the arguments and retrieve the array and its size
     * 
     */
    resultsWrapper *results = (resultsWrapper *)args;
    int size = results->size;
    int *array = results->array;
    int *sum = (int *)malloc(sizeof(int *));
    *sum = 0;

    /**
     * @brief sum its elements up
     * 
     */
    for (int i = 0; i < size; i++)
    {
        *sum += array[i];
    }

    free(results);
    free(array);

    /**
     * @brief return the result
     * 
     */
    pthread_exit((void *)sum);
}

/**
 * @brief populates the given vector with random numbers
 * 
 * @param array the target array
 * @param size the size of the array
 */
void populateWithRand(int *array, int size)
{
    for (int i = 0; i < size; i++)
    {
        array[i] = rand() % MAX_RAND;
    }
    printf("Finished populating vector\n");
}

/**
 * @brief this prints a given array
 * 
 * @param array the array to be printed
 * @param size the size of the array
 */
void printArray(int *array, int size)
{
    printf("Array: \n");
    for (int i = 0; i < size; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");
}

/**
 * @brief this creates a normal partition, i.e. a partition containing size/threads elements
 * 
 * @param index the current index in array  
 * @param quotient the quotient
 * @return wrapper returns a new "pair"
 */
wrapper createNormalPartition(int index, int quotient)
{
    wrapper normalPartition;
    normalPartition.startIndex = index;
    normalPartition.endIndex = index + quotient - 1;
    printf("    Created normal partition (%d, %d)\n", normalPartition.startIndex, normalPartition.endIndex);
    return normalPartition;
}

/**
 * @brief this creates an overloaded partition, i.e. a partition containing size/threads+1 elements. We use overloaded partitions to spread
 * the load amongst r threads, where r is size%threads
 * 
 * @param index the current index in the array
 * @param quotient the quotient
 * @return wrapper returns a new "overloaded pair"
 */
wrapper createOverloadedPartition(int index, int quotient)
{
    wrapper normalPartition;
    normalPartition.startIndex = index;
    normalPartition.endIndex = index + quotient;
    printf("    Created normal partition (%d, %d)\n", normalPartition.startIndex, normalPartition.endIndex);
    return normalPartition;
}

/**
 * @brief core function. Splits the entire array by index to each thread
 * 
 * @return wrapper* returns an array of partitions to be used by threads
 */
wrapper *createPartitions()
{
    printf("Creating partitions......\n");
    /**
     * @brief compute the quotient, the remainder and initialize
     * 
     */
    int quotient = size / numberOfThreads;
    int remainder = size % numberOfThreads;
    int last = 0;

    /**
     * @brief create the array of partitions
     * 
     */
    wrapper *partitions = (wrapper *)malloc(sizeof(wrapper));

    /**
     * @brief populate the previously created array. If the we have a remainder, the last r threads will have an extra computation to perform.
     * 
     */
    for (int i = 0; i < numberOfThreads; i++)
    {
        /**
             * @brief check the current index and create the appropriate partitions
             * 
             */
        if (i < numberOfThreads - remainder)
        {
            partitions[i] = createNormalPartition(last, quotient);
            last += quotient;
            continue;
        }
        wrapper temp = createOverloadedPartition(last, quotient);

        partitions[i] = temp;
        last += quotient + 1;
    }
    printf("Finished creating partitions......\n");

    /**
     * @brief return the previously-populated partitions
     * 
     */
    return partitions;
}

/**
 * @brief this is a utility function. This creates the threads and assigns them the working partition
 * 
 * @param threads the array of threads
 */
void createThreads(pthread_t *threads)
{
    /**
     * @brief create a dummy wrapper to store the pairs at every step
     * 
     */
    wrapper *partitions = createPartitions();

    printf("Creating threads......\n");

    /**
     * @brief create some threads
     * 
     */
    for (int i = 0; i < numberOfThreads; i++)
    {
        pthread_create(&threads[i], NULL, sum_runner, (void *)&partitions[i]);
        printf("    Created thread %lu\n", threads[i]);
    }
    free(partitions);
    printf("Finished creating threads......\n");
}

/**
 * @brief this is a utility function. This performs join in the threads and stores the sums computed
 * 
 * @param threads the array of threads
 * @return int* the array of computed sums
 */
int *startThreads(pthread_t *threads)
{
    printf("Starting threads...\n");
    /**
     * @brief initialize local variables
     * 
     */
    int *temp;
    int *results = (int *)malloc(sizeof(int *) * numberOfThreads);

    /**
     * @brief performs join on threads and store the sums computed
     * 
     */
    for (int i = 0; i < numberOfThreads; i++)
    {
        temp = (int *)malloc(sizeof(temp));
        printf("    Starting thread %lu\n", threads[i]);
        pthread_join(threads[i], (void **)&temp);
        results[i] = *temp;
        free(temp);
    }

    printf("Exiting...\n");
    /**
     * @brief return the array of computed sums
     * 
     */
    return results;
}

/**
 * @brief this function calls the utility functions and computes the final sum using a separate thread
 * 
 * @return int 
 */
int run()
{
    /**
     * @brief create the threads ids array
     * 
     */
    int *results;
    int *finalResult;
    pthread_t *threads = (pthread_t *)malloc(sizeof(pthread_t *));
    pthread_t summingThread;

    /**
     * @brief pass the threads id's to create them
     * 
     */
    createThreads(threads);

    /**
     * @brief get the sums
     * 
     */
    results = startThreads(threads);

    /**
     * @brief print the array 
     * 
     */
    printArray(results, numberOfThreads);

    /**
     * @brief create a new thread and let him compute the final sum
     * 
     */
    resultsWrapper *resultData = (resultsWrapper *)malloc(sizeof(resultsWrapper *));
    resultData->size = numberOfThreads;
    resultData->array = results;

    /**
     * @brief add up the sums computed by the threads
     * 
     */
    pthread_create(&summingThread, NULL, results_sum_runner, (void *)resultData);
    pthread_join(summingThread, (void *)&finalResult);

    /**
     * @brief return the sum
     * 
     */
    free(threads);
    free(resultData);
    free(results);
    return *finalResult;
}

/**
 * @brief this is the entry point
 * 
 * @return int success
 */
int main()
{
    /**
     * @brief initialize variables, run the program and print the result
     * 
     */
    size = 47;
    array = calloc(sizeof(array), size);
    populateWithRand(array, size);
    printArray(array, size);
    int sum;
    sum = run();
    free(array);
    printf("Sum of the array is %d\n", sum);
}

样例输出:

Finished populating vector
Array:
83 86 77 15 93 35 86 92 49 21 62 27 90 59 63 26 40 26 72 36 11 68 67 29 82 30 62 23 67 35 29 2 22 58 69 67 93 56 11 42 29 73 21 19 84 37 98
Creating partitions......
    Created normal partition (0, 14)
    Created normal partition (15, 30)
    Created normal partition (31, 46)
Finished creating partitions......
Creating threads......
    Created thread 139720910055168
    Created thread 139720901662464
    Created thread 139720893269760
Finished creating threads......
Starting threads...
    Starting thread 139720910055168
    Starting thread 139720901662464
    Starting thread 139720893269760
Exiting...
Array:
875 674 683
Sum of the array is 2232

Valgrind的报告:

==4725== Memcheck, a memory error detector
==4725== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.==4725== Using Valgrind-3.14.0.GIT and LibVEX; rerun with -h for copyright info==4725== Command: ./counter
==4725==Finished populating vector
Array:
83 86 77 15 93 35 86 92 49 21 62 27 90 59 63 26 40 26 72 36 11 68 67 29 82 30 62 23 67 35 29 2 22 58 69 67 93 56 11 42 29 73 21 19 84 37 98
Creating partitions......
    Created normal partition (0, 14)
    Created normal partition (15, 30)
==4725== Invalid write of size 8
==4725==    at 0x109505: createPartitions (counter.c:215)
==4725==    by 0x109550: createThreads (counter.c:238)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61698 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10948F: createPartitions (counter.c:195)
==4725==    by 0x109550: createThreads (counter.c:238)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
    Created normal partition (31, 46)
Finished creating partitions......
Creating threads......
    Created thread 90576640
==4725== Invalid write of size 8
==4725==    at 0x48812C8: pthread_create@@GLIBC_2.2.5 (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x1095A8: createThreads (counter.c:248)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10972A: run (counter.c:305)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
==4725== Invalid read of size 8
==4725==    at 0x1095BD: createThreads (counter.c:249)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10972A: run (counter.c:305)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
    Created thread 98969344
    Created thread 107362048
==4725== Thread 3:
==4725== Invalid read of size 4
==4725==    at 0x1091F1: sum_runner (counter.c:52)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Address 0x4a61698 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10948F: createPartitions (counter.c:195)
==4725==    by 0x109550: createThreads (counter.c:238)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
==4725== Invalid read of size 4
==4725==    at 0x1091FA: sum_runner (counter.c:53)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Address 0x4a6169c is 4 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10948F: createPartitions (counter.c:195)
==4725==    by 0x109550: createThreads (counter.c:238)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
Finished creating threads......
Starting threads...
    Starting thread 90576640
==4725== Thread 1:
==4725== Invalid read of size 8
==4725==    at 0x10966B: startThreads (counter.c:278)
==4725==    by 0x109746: run (counter.c:318)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10972A: run (counter.c:305)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
    Starting thread 98969344
==4725== Invalid read of size 8
==4725==    at 0x109696: startThreads (counter.c:279)
==4725==    by 0x109746: run (counter.c:318)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10972A: run (counter.c:305)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
    Starting thread 107362048
Exiting...
Array:
875 674 683
==4725== Invalid write of size 4
==4725==    at 0x109777: run (counter.c:331)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a62508 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x109768: run (counter.c:330)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
==4725== Thread 2:
==4725== Invalid read of size 4
==4725==    at 0x10926E: results_sum_runner (counter.c:87)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Address 0x4a62508 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x109768: run (counter.c:330)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
==4725== Thread 1:
==4725== Invalid free() / delete / delete[] / realloc()
==4725==    at 0x48389AB: free (vg_replace_malloc.c:530)
==4725==    by 0x1097CE: run (counter.c:346)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a62500 is 0 bytes inside a block of size 8 free'd
==4725==    at 0x48389AB: free (vg_replace_malloc.c:530)
==4725==    by 0x1092DB: results_sum_runner (counter.c:101)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Block was alloc'd at
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x109768: run (counter.c:330)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
==4725== Invalid free() / delete / delete[] / realloc()
==4725==    at 0x48389AB: free (vg_replace_malloc.c:530)
==4725==    by 0x1097DA: run (counter.c:347)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61b20 is 0 bytes inside a block of size 24 free'd
==4725==    at 0x48389AB: free (vg_replace_malloc.c:530)
==4725==    by 0x1092E7: results_sum_runner (counter.c:102)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Block was alloc'd at
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x109638: startThreads (counter.c:269)
==4725==    by 0x109746: run (counter.c:318)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
Sum of the array is 2232
==4725==
==4725== HEAP SUMMARY:
==4725==     in use at exit: 1,652 bytes in 8 blocks
==4725==   total heap usage: 21 allocs, 15 frees, 3,996 bytes allocated
==4725==
==4725== LEAK SUMMARY:
==4725==    definitely lost: 32 bytes in 4 blocks
==4725==    indirectly lost: 0 bytes in 0 blocks
==4725==      possibly lost: 0 bytes in 0 blocks
==4725==    still reachable: 1,620 bytes in 4 blocks
==4725==         suppressed: 0 bytes in 0 blocks
==4725== Rerun with --leak-check=full to see details of leaked memory
==4725==
==4725== For counts of detected and suppressed errors, rerun with: -v
==4725== ERROR SUMMARY: 20 errors from 11 contexts (suppressed: 0 from 0)
2个回答

1

在寻找泄漏之前,请先解决您的无效写入问题。

首先,在createPartitions函数中,您为partitions分配了内存,但只足以存储单个wrapper

wrapper *partitions = (wrapper *)malloc(sizeof(wrapper));

你需要运行一个循环,循环次数为numberOfThreads(3),并将其复制到上述数组中。第二个及以后的写入超出了数组范围,导致了invalid write
你需要为partitions分配更多内存,例如:
wrapper *partitions = (wrapper *)malloc(numberOfThreads*sizeof(wrapper));

但是为什么这不会崩溃呢?我完全不理解。 - CyberFox
1
这是未定义的行为。任何事情都有可能发生。它可能会崩溃,或者你可能很幸运地没有遇到问题。 - Paul Floyd

1

这里肯定存在一些内存泄漏问题,但更紧迫的问题是多个实例读取/写入超出分配内存末尾和重复释放。

让我们从上到下依次解决这些问题:

==4725== Invalid write of size 8
==4725==    at 0x109505: createPartitions (counter.c:215)
==4725==    by 0x109550: createThreads (counter.c:238)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61698 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10948F: createPartitions (counter.c:195)
==4725==    by 0x109550: createThreads (counter.c:238)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==

这是在抱怨写入超过分配的空间。以下是既进行分配又进行无效访问的代码:
wrapper *partitions = (wrapper *)malloc(sizeof(wrapper));

for (int i = 0; i < numberOfThreads; i++)
{
    if (i < numberOfThreads - remainder)
    {
        partitions[i] = createNormalPartition(last, quotient);
        last += quotient;
        continue;
    }
    wrapper temp = createOverloadedPartition(last, quotient);

    partitions[i] = temp;
    last += quotient + 1;
}

您正在为一个wrapper的单个实例分配空间,但是却像它是numberOfThreads个实例的数组一样进行写入。您需要为这么多个实例分配空间:
wrapper *partitions = malloc(sizeof(wrapper) * numberOfThreads);

下一个:

下一个:

==4725== Invalid write of size 8
==4725==    at 0x48812C8: pthread_create@@GLIBC_2.2.5 (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x1095A8: createThreads (counter.c:248)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10972A: run (counter.c:305)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
==4725== Invalid read of size 8
==4725==    at 0x1095BD: createThreads (counter.c:249)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10972A: run (counter.c:305)
==4725==    by 0x10985C: main (counter.c:367)
==4725==

又一次无效写入和无效读取。这是在run中分配的:

pthread_t *threads = (pthread_t *)malloc(sizeof(pthread_t *));

createThreads中的读取/写入:

for (int i = 0; i < numberOfThreads; i++)
{
    pthread_create(&threads[i], NULL, sum_runner, (void *)&partitions[i]);
    printf("    Created thread %lu\n", threads[i]);
}

与之前一样,您正在为单个实例分配空间,而不是数组。此外,您正在为pthread_t *分配空间,而不是pthread_t,这很可能太小了。更改分配以腾出数组空间,并使用对象类型而不是指针类型:
pthread_t *threads = malloc(sizeof(pthread_t) * numberOfThreads);

下一步:
==4725== Invalid read of size 4
==4725==    at 0x1091F1: sum_runner (counter.c:52)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Address 0x4a61698 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10948F: createPartitions (counter.c:195)
==4725==    by 0x109550: createThreads (counter.c:238)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
==4725== Invalid read of size 4
==4725==    at 0x1091FA: sum_runner (counter.c:53)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Address 0x4a6169c is 4 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10948F: createPartitions (counter.c:195)
==4725==    by 0x109550: createThreads (counter.c:238)
==4725==    by 0x10973A: run (counter.c:312)
==4725==    by 0x10985C: main (counter.c:367)
==4725==

一对无效读取源自相同的分配和访问,位于相邻的行中。这是第一条消息中的相同无效分配,我们已经解决了。接下来是:
==4725== Thread 1:
==4725== Invalid read of size 8
==4725==    at 0x10966B: startThreads (counter.c:278)
==4725==    by 0x109746: run (counter.c:318)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10972A: run (counter.c:305)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
    Starting thread 98969344
==4725== Invalid read of size 8
==4725==    at 0x109696: startThreads (counter.c:279)
==4725==    by 0x109746: run (counter.c:318)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x10972A: run (counter.c:305)
==4725==    by 0x10985C: main (counter.c:367)
==4725==

一对无效读取指向第二条消息中相同的错误分配。这也已经得到解决。接下来:
==4725== Invalid write of size 4
==4725==    at 0x109777: run (counter.c:331)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a62508 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x109768: run (counter.c:330)
==4725==    by 0x10985C: main (counter.c:367)
==4725==
==4725== Thread 2:
==4725== Invalid read of size 4
==4725==    at 0x10926E: results_sum_runner (counter.c:87)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Address 0x4a62508 is 0 bytes after a block of size 8 alloc'd
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x109768: run (counter.c:330)
==4725==    by 0x10985C: main (counter.c:367)

源自同一分配的无效读写。 这是在run中的分配:

resultsWrapper *resultData = (resultsWrapper *)malloc(sizeof(resultsWrapper *));

results_sum_runner中读取:

resultsWrapper *results = (resultsWrapper *)args;
int size = results->size;

而且在 run 中写入:

resultData->size = numberOfThreads;

在这里,你正在为指向resultsWrapper指针分配空间,并非resultsWrapper实例。请按以下方式修复分配:
resultsWrapper *resultData = malloc(sizeof(resultsWrapper));

下一个:
==4725== Invalid free() / delete / delete[] / realloc()
==4725==    at 0x48389AB: free (vg_replace_malloc.c:530)
==4725==    by 0x1097CE: run (counter.c:346)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a62500 is 0 bytes inside a block of size 8 free'd
==4725==    at 0x48389AB: free (vg_replace_malloc.c:530)
==4725==    by 0x1092DB: results_sum_runner (counter.c:101)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Block was alloc'd at
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x109768: run (counter.c:330)
==4725==    by 0x10985C: main (counter.c:367)
==4725==

这里是双重释放。分配和第二个free都在run函数中:

resultsWrapper *resultData = (resultsWrapper *)malloc(sizeof(resultsWrapper *));
resultData->size = numberOfThreads;
resultData->array = results;

pthread_create(&summingThread, NULL, results_sum_runner, (void *)resultData);
pthread_join(summingThread, (void *)&finalResult);

free(threads);
free(resultData);
free(results);

以下是在results_sum_runner中第一个free
void *results_sum_runner(void *args)
{
    resultsWrapper *results = (resultsWrapper *)args;
    int size = results->size;
    int *array = results->array;
    int *sum = (int *)malloc(sizeof(int *));
    *sum = 0;

    for (int i = 0; i < size; i++)
    {
        *sum += array[i];
    }

    free(results);
    free(array);

    pthread_exit((void *)sum);
}

在这里,你正在为resultData分配内存并将其传递给线程函数results_sum_runner。该线程释放了内存,但调用线程之后也会释放内存。在run中删除free(resultData)或在results_sum_runner中删除free(results)
==4725== Invalid free() / delete / delete[] / realloc()
==4725==    at 0x48389AB: free (vg_replace_malloc.c:530)
==4725==    by 0x1097DA: run (counter.c:347)
==4725==    by 0x10985C: main (counter.c:367)
==4725==  Address 0x4a61b20 is 0 bytes inside a block of size 24 free'd
==4725==    at 0x48389AB: free (vg_replace_malloc.c:530)
==4725==    by 0x1092E7: results_sum_runner (counter.c:102)
==4725==    by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725==    by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725==  Block was alloc'd at
==4725==    at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725==    by 0x109638: startThreads (counter.c:269)
==4725==    by 0x109746: run (counter.c:318)
==4725==    by 0x10985C: main (counter.c:367)
==4725==

另一个类似于上一个的双重释放问题。在run中的free(results)和在results_sum_runner中的free(array)引用了相同的内存,因此需要删除其中一个。
现在,如果我们使用这些更改进行编译并在valgrind下再次运行,会出现一个问题:
==24305== Invalid read of size 4
==24305==    at 0x40090E: sum_runner (x1.c:52)
==24305==    by 0x4E416B9: start_thread (pthread_create.c:333)
==24305==  Address 0x54216a8 is 8 bytes inside a block of size 24 free'd
==24305==    at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24305==    by 0x400CEF: createThreads (x1.c:251)
==24305==    by 0x400E3C: run (x1.c:312)
==24305==    by 0x400F5C: main (x1.c:367)
==24305==  Block was alloc'd at
==24305==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24305==    by 0x400B98: createPartitions (x1.c:195)
==24305==    by 0x400C57: createThreads (x1.c:238)
==24305==    by 0x400E3C: run (x1.c:312)
==24305==    by 0x400F5C: main (x1.c:367)
==24305== 
==24305== Invalid read of size 4
==24305==    at 0x400917: sum_runner (x1.c:53)
==24305==    by 0x4E416B9: start_thread (pthread_create.c:333)
==24305==  Address 0x54216ac is 12 bytes inside a block of size 24 free'd
==24305==    at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24305==    by 0x400CEF: createThreads (x1.c:251)
==24305==    by 0x400E3C: run (x1.c:312)
==24305==    by 0x400F5C: main (x1.c:367)
==24305==  Block was alloc'd at
==24305==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24305==    by 0x400B98: createPartitions (x1.c:195)
==24305==    by 0x400C57: createThreads (x1.c:238)
==24305==    by 0x400E3C: run (x1.c:312)
==24305==    by 0x400F5C: main (x1.c:367)
==24305== 

这里我们有一对来自已释放内存块的读取。读取发生在sum_runner函数中:
wrapper *data = (wrapper *)args;
int currentIndex = data->startIndex;
int endIndex = data->endIndex;

createThreads 中的 free:

for (int i = 0; i < numberOfThreads; i++)
{
    pthread_create(&threads[i], NULL, sum_runner, (void *)&partitions[i]);
    printf("    Created thread %lu\n", threads[i]);
}
free(partitions);

根据您在代码其他地方的评论,您似乎认为调用pthread_join会启动一个线程。实际上并非如此。调用pthread_create才真正启动线程,而pthread_join告诉调用线程等待给定线程完成。因此,在线程有机会使用它之前,您最终会释放该内存。

因此,请删除此free并将createThreads更改为返回此指针。然后在run(其中调用createThreads)中,在调用startThreads之后进行释放(实际上应将其重命名为类似于waitForThreadsToFinish的内容):

wrapper *createThreads(pthread_t *threads)
{
    wrapper *partitions = createPartitions();

    printf("Creating threads......\n");

    for (int i = 0; i < numberOfThreads; i++)
    {
        pthread_create(&threads[i], NULL, sum_runner, (void *)&partitions[i]);
        printf("    Created thread %lu\n", threads[i]);
    }
    // remove free
    printf("Finished creating threads......\n");
    return partitions;
}

...

int run() 
{
    ...

    wrapper *partitions = createThreads(threads);
    results = startThreads(threads);
    free(partitions);

这样应该就解决了无效访问的问题。

还有一些地方你分配了一个 int *(或者是一个 int * 数组)的空间,但你应该分配一个或多个 int 的空间。在你的系统上这不会引起问题,因为一个 int * 至少和一个 int 一样大,但无论如何你都应该使用正确的类型。

同时请注意,我提出的更改去掉了 malloc 返回值的强制转换。关于此,请参见 Do I cast the result of malloc? 获取更多细节。

如果你向 valgrind 传递 --leak-check=full,你仍然可以找到一些内存泄漏,但我会把它作为读者的练习留给你。


我真的需要更加注意资源分配。非常感谢您,先生! - CyberFox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接