内存泄漏和指针
Memory leaks and pointers
我已经实现了一个程序,它使用线程将数组的元素相加。每个线程添加数组一部分的元素并将其放入数组中。最后,另一个线程总结了这些。
这个程序似乎可以工作,但我有大量内存泄漏似乎无法修复。我正在使用 valgrind 来检测那些内存泄漏。我敢打赌我的指针知识缺少一些关键特征。
我也曾尝试在线程 returns 之前释放结构,但这会转储核心。我试图在分配内存的任何地方释放内存,但 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)
在查找泄漏之前修复您的 invalid write
。
首先,在 createPartitions
中,您为 partitions
分配了内存,但只够存储一个 wrapper
wrapper *partitions = (wrapper *)malloc(sizeof(wrapper));
然后你有一个循环运行 numberOfThreads
次 (3) 并复制到上面的数组中。第二次及以后的写入超出了数组的范围,并导致 invalid write
.
您需要为 partitions
分配更多内存,例如
wrapper *partitions = (wrapper *)malloc(numberOfThreads*sizeof(wrapper));
这里肯定有一些内存泄漏,但您遇到的更紧迫的问题是多次读/写实例超过分配内存的末尾和双重释放。
让我们从顶部开始一次看这些:
==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
的 单个 实例分配 space,但写入它就好像它是 [=33= 的数组一样] 实例。您需要为这么多实例分配 space:
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]);
}
和以前一样,您为 单个 实例分配 space,而不是数组。此外,您为 pthread_t *
而不是 pthread_t
分配了 space,这可能太小了。更改分配以对数组进行 space,并使用对象类型,而不是指针类型:
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;
在这里,您正在为 指针 分配 space 到 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
中的免费:
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
改成return这个指针。然后在 run
(调用 createThreads
的地方)并在调用 startThreads
之后在这里做 free (实际上应该重命名为 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 *
的数组)分配 space,您应该为其中一个或一个分配 space更多 int
。它不会在您的系统上造成问题,因为 int *
至少与 int
一样大,但无论如何您仍应使用正确的类型。
另请注意,我提议的更改删除了对 malloc
的 return 值的强制转换。有关详细信息,请参阅 Do I cast the result of malloc?。
如果将 --leak-check=full
传递给 valgrind,您仍然可以发现一些内存泄漏,但我将把它留给 reader.
作为练习。
我已经实现了一个程序,它使用线程将数组的元素相加。每个线程添加数组一部分的元素并将其放入数组中。最后,另一个线程总结了这些。
这个程序似乎可以工作,但我有大量内存泄漏似乎无法修复。我正在使用 valgrind 来检测那些内存泄漏。我敢打赌我的指针知识缺少一些关键特征。
我也曾尝试在线程 returns 之前释放结构,但这会转储核心。我试图在分配内存的任何地方释放内存,但 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)
在查找泄漏之前修复您的 invalid write
。
首先,在 createPartitions
中,您为 partitions
分配了内存,但只够存储一个 wrapper
wrapper *partitions = (wrapper *)malloc(sizeof(wrapper));
然后你有一个循环运行 numberOfThreads
次 (3) 并复制到上面的数组中。第二次及以后的写入超出了数组的范围,并导致 invalid write
.
您需要为 partitions
分配更多内存,例如
wrapper *partitions = (wrapper *)malloc(numberOfThreads*sizeof(wrapper));
这里肯定有一些内存泄漏,但您遇到的更紧迫的问题是多次读/写实例超过分配内存的末尾和双重释放。
让我们从顶部开始一次看这些:
==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
的 单个 实例分配 space,但写入它就好像它是 [=33= 的数组一样] 实例。您需要为这么多实例分配 space:
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]);
}
和以前一样,您为 单个 实例分配 space,而不是数组。此外,您为 pthread_t *
而不是 pthread_t
分配了 space,这可能太小了。更改分配以对数组进行 space,并使用对象类型,而不是指针类型:
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;
在这里,您正在为 指针 分配 space 到 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
中的免费:
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
改成return这个指针。然后在 run
(调用 createThreads
的地方)并在调用 startThreads
之后在这里做 free (实际上应该重命名为 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 *
的数组)分配 space,您应该为其中一个或一个分配 space更多 int
。它不会在您的系统上造成问题,因为 int *
至少与 int
一样大,但无论如何您仍应使用正确的类型。
另请注意,我提议的更改删除了对 malloc
的 return 值的强制转换。有关详细信息,请参阅 Do I cast the result of malloc?。
如果将 --leak-check=full
传递给 valgrind,您仍然可以发现一些内存泄漏,但我将把它留给 reader.