Fix memory leak when a child exits without wait()-ed
Change-Id: I8ad9e20e3f3e6f406548a6c4de2bf4dc07c40b0e Refs: #1349
This commit is contained in:
committed by
Masamichi Takagi
parent
1d1ec39a27
commit
b3cbdeec84
@ -1346,8 +1346,8 @@ void terminate(int rc, int sig)
|
|||||||
|
|
||||||
if (child->ppid_parent == proc &&
|
if (child->ppid_parent == proc &&
|
||||||
child->status == PS_ZOMBIE) {
|
child->status == PS_ZOMBIE) {
|
||||||
list_del(&child->hash_list);
|
list_del_init(&child->hash_list);
|
||||||
list_del(&child->siblings_list);
|
list_del_init(&child->siblings_list);
|
||||||
free_child = 1;
|
free_child = 1;
|
||||||
}
|
}
|
||||||
else if (child->ppid_parent == proc) {
|
else if (child->ppid_parent == proc) {
|
||||||
@ -1377,7 +1377,7 @@ void terminate(int rc, int sig)
|
|||||||
&updatelock);
|
&updatelock);
|
||||||
|
|
||||||
if (free_child)
|
if (free_child)
|
||||||
kfree(child);
|
release_process(child);
|
||||||
}
|
}
|
||||||
mcs_rwlock_writer_unlock(&resource_set->process_hash->lock[i],
|
mcs_rwlock_writer_unlock(&resource_set->process_hash->lock[i],
|
||||||
&lock);
|
&lock);
|
||||||
|
|||||||
10
test/issues/1349/Makefile
Normal file
10
test/issues/1349/Makefile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Makefile COPYRIGHT FUJITSU LIMITED 2020
|
||||||
|
CC?=gcc
|
||||||
|
LDFLAGS=-lpthread
|
||||||
|
CFLAGS=-Wall -O0 -ggdb3
|
||||||
|
|
||||||
|
all: do_fork6
|
||||||
|
do_fork6: do_fork6.o
|
||||||
|
do_fork6.o: do_fork6.c
|
||||||
|
clean:
|
||||||
|
rm -f do_fork6.o do_fork6
|
||||||
18
test/issues/1349/README
Normal file
18
test/issues/1349/README
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
==========
|
||||||
|
How to run
|
||||||
|
==========
|
||||||
|
|
||||||
|
(1) cd <mckernel>
|
||||||
|
(2) Build McKernel
|
||||||
|
(3) cd <mckernel>/test/issues/1349
|
||||||
|
(4) make
|
||||||
|
(5) sh ./run.sh
|
||||||
|
|
||||||
|
============
|
||||||
|
What to test
|
||||||
|
============
|
||||||
|
|
||||||
|
Check for memory leak when child processes exit without reaped by the parent's wait.
|
||||||
|
|
||||||
|
--
|
||||||
|
README COPYRIGHT FUJITSU LIMITED 2020
|
||||||
288
test/issues/1349/do_fork6.c
Normal file
288
test/issues/1349/do_fork6.c
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/* do_fork6.c COPYRIGHT FUJITSU LIMITED 2020 */
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <linux/futex.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define FORK1() \
|
||||||
|
do { \
|
||||||
|
int pid = fork(); \
|
||||||
|
if (pid < 0) { \
|
||||||
|
onError("fork"); \
|
||||||
|
} else if (pid == 0) { \
|
||||||
|
printf("grandchild\n"); \
|
||||||
|
exit(0); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define FORK10() \
|
||||||
|
do { \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
FORK1(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define FORK100() \
|
||||||
|
do { \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
FORK10(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
enum fin_mode {
|
||||||
|
FIN_MODE_NULL,
|
||||||
|
FIN_MODE_EXIT,
|
||||||
|
FIN_MODE_WAIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAXNUMTHREADS 256
|
||||||
|
#define DEFAULTTIMETOWAIT 500
|
||||||
|
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
int numthreads = 1;
|
||||||
|
enum fin_mode fin_mode = FIN_MODE_NULL;
|
||||||
|
int timetowait = DEFAULTTIMETOWAIT;
|
||||||
|
|
||||||
|
struct timeval timeBeforeFork;
|
||||||
|
struct timeval timeBeforeTest;
|
||||||
|
struct timeval timeAfterTest;
|
||||||
|
|
||||||
|
#define LAPTIME_MS(start, stop) \
|
||||||
|
((stop.tv_sec - start.tv_sec) * 1000 \
|
||||||
|
+ (stop.tv_usec - start.tv_usec) / 1000)
|
||||||
|
|
||||||
|
struct Thread {
|
||||||
|
int tid;
|
||||||
|
pthread_t pthread;
|
||||||
|
} thread[MAXNUMTHREADS];
|
||||||
|
|
||||||
|
pthread_barrier_t barrier;
|
||||||
|
|
||||||
|
void onError(char *message)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: %s: %m\n", argv[0], message);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitChildren(void)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
int status = 0;
|
||||||
|
pid_t pid = wait(&status);
|
||||||
|
|
||||||
|
if (pid == -1) {
|
||||||
|
if (errno == ECHILD) {
|
||||||
|
return;
|
||||||
|
} else if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
onError("wait");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void examinerProcess(pid_t subject)
|
||||||
|
{
|
||||||
|
printf("[%d] I am the examiner for %d.\n", getpid(), subject);
|
||||||
|
waitChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
void subjectTask(struct Thread *thread)
|
||||||
|
{
|
||||||
|
pthread_barrier_wait(&barrier);
|
||||||
|
|
||||||
|
gettimeofday(&timeBeforeTest, NULL);
|
||||||
|
printf("[%d] setup: %ld ms\n",
|
||||||
|
thread->tid,
|
||||||
|
LAPTIME_MS(timeBeforeFork, timeBeforeTest));
|
||||||
|
printf("[%d] START TEST\n", thread->tid);
|
||||||
|
|
||||||
|
FORK100();
|
||||||
|
|
||||||
|
printf("[%d] END FORK\n", thread->tid);
|
||||||
|
|
||||||
|
{
|
||||||
|
struct timespec req, rem;
|
||||||
|
|
||||||
|
req.tv_sec = timetowait / 1000;
|
||||||
|
req.tv_nsec = (timetowait % 1000) * 1000000;
|
||||||
|
if (nanosleep(&req, &rem) < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"nanosleep is interrupted, but ignore\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[%d] FINISH CHILDREN\n", thread->tid);
|
||||||
|
switch (fin_mode) {
|
||||||
|
case FIN_MODE_EXIT:
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
case FIN_MODE_WAIT:
|
||||||
|
waitChildren();
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%d(%d) TEST FAIL OVERRUN\n", thread->tid, fin_mode);
|
||||||
|
gettimeofday(&timeAfterTest, NULL);
|
||||||
|
for (;;)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subjectCleanup(void *arg)
|
||||||
|
{
|
||||||
|
struct Thread *thread = (struct Thread *)arg;
|
||||||
|
|
||||||
|
printf("[%d] cleanup\n", thread->tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *subjectThread(void *arg)
|
||||||
|
{
|
||||||
|
struct Thread *thread = (struct Thread *)arg;
|
||||||
|
|
||||||
|
printf("[%d] I am a %s %d, %lx %lx\n",
|
||||||
|
getpid(),
|
||||||
|
__func__,
|
||||||
|
thread->tid,
|
||||||
|
thread->pthread,
|
||||||
|
pthread_self());
|
||||||
|
|
||||||
|
pthread_cleanup_push(subjectCleanup, arg);
|
||||||
|
|
||||||
|
pthread_barrier_wait(&barrier);
|
||||||
|
|
||||||
|
subjectTask(thread); //< no return
|
||||||
|
|
||||||
|
pthread_cleanup_pop(1);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createThreads(void)
|
||||||
|
{
|
||||||
|
if (pthread_barrier_init(&barrier, NULL, numthreads)) {
|
||||||
|
onError("pthread_barrier_init fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 1; i < numthreads; i++) {
|
||||||
|
int rval;
|
||||||
|
|
||||||
|
thread[i].tid = i;
|
||||||
|
rval = pthread_create(&thread[i].pthread,
|
||||||
|
NULL,
|
||||||
|
subjectThread,
|
||||||
|
&thread[i]);
|
||||||
|
if (rval) {
|
||||||
|
onError("pthread_create fail");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thread[0].tid = 0;
|
||||||
|
thread[0].pthread = pthread_self();
|
||||||
|
subjectThread(&thread[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int _argc, char **_argv)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
argc = _argc;
|
||||||
|
argv = _argv;
|
||||||
|
|
||||||
|
printf("DO FORK6\n");
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp("-nt", argv[i]) == 0) {
|
||||||
|
i++;
|
||||||
|
if (i < argc) {
|
||||||
|
numthreads = atoi(argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%s: num threads required\n", argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (strcmp("-mode-exit", argv[i]) == 0) {
|
||||||
|
fin_mode = FIN_MODE_EXIT;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strcmp("-mode-wait", argv[i]) == 0) {
|
||||||
|
fin_mode = FIN_MODE_WAIT;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strcmp("-t", argv[i]) == 0) {
|
||||||
|
i++;
|
||||||
|
if (i < argc) {
|
||||||
|
timetowait = atoi(argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s"
|
||||||
|
" -nt <num threads>"
|
||||||
|
" -t <time to wait(msec)>"
|
||||||
|
" {-mode-exit | -mode-wait}\n",
|
||||||
|
argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numthreads < 1 || numthreads > MAXNUMTHREADS) {
|
||||||
|
fprintf(stderr, "%s: invalid num threads\n", argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fin_mode == FIN_MODE_NULL) {
|
||||||
|
fprintf(stderr, "%s: invalid mode\n", argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("NUMTHREADS: %d\n", numthreads);
|
||||||
|
printf("TIMETOWAIT: %d msec\n", timetowait);
|
||||||
|
printf("MODE: %s\n",
|
||||||
|
(fin_mode == FIN_MODE_EXIT) ? "exit" :
|
||||||
|
(fin_mode == FIN_MODE_WAIT) ? "wait" :
|
||||||
|
"null");
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
onError("fork");
|
||||||
|
} else if (pid == 0) {
|
||||||
|
createThreads(); //< no return
|
||||||
|
//joinThreads();
|
||||||
|
} else {
|
||||||
|
examinerProcess(pid);
|
||||||
|
printf("FINISH PARENT\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
16
test/issues/1349/result.log
Normal file
16
test/issues/1349/result.log
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[root@localhost mckernel]# cd test/issues/1349/
|
||||||
|
[root@localhost 1349]# make
|
||||||
|
cc -Wall -O0 -ggdb3 -c -o do_fork6.o do_fork6.c
|
||||||
|
cc -lpthread do_fork6.o -o do_fork6
|
||||||
|
[root@localhost 1349]# sh run.sh
|
||||||
|
mcstop+release.sh ... done
|
||||||
|
mcreboot.sh -c 12-59 -m 512M@4 ... done
|
||||||
|
(an omission)
|
||||||
|
[0] END FORK
|
||||||
|
grandchild
|
||||||
|
[0] FINISH CHILDREN
|
||||||
|
FINISH PARENT
|
||||||
|
OK
|
||||||
|
|
||||||
|
--
|
||||||
|
result.log COPYRIGHT FUJITSU LIMITED 2020
|
||||||
29
test/issues/1349/run.sh
Normal file
29
test/issues/1349/run.sh
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# run.sh COPYRIGHT FUJITSU LIMITED 2020
|
||||||
|
test_dir=$(dirname "${BASH_SOURCE[0]}")
|
||||||
|
. "${test_dir}/../../common.sh"
|
||||||
|
|
||||||
|
# init
|
||||||
|
meminfo="/sys/devices/virtual/mcos/mcos0/sys/devices/system/node/node0/meminfo"
|
||||||
|
"${MCEXEC}" 0 ./do_fork6 -nt 1 -t $((1000 * 10)) -mode-wait
|
||||||
|
sleep 1
|
||||||
|
exp_free_mem=`cat "$meminfo" | grep MemFree:`
|
||||||
|
|
||||||
|
# run
|
||||||
|
"${MCEXEC}" 0 ./do_fork6 -nt 1 -t $((1000 * 10)) -mode-wait
|
||||||
|
sleep 1
|
||||||
|
free_mem=`cat "$meminfo" | grep MemFree:`
|
||||||
|
if [ "$exp_free_mem" != "$free_mem" ]; then
|
||||||
|
echo "NG: detected memory leak."
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${MCEXEC}" 0 ./do_fork6 -nt 1 -t $((1000 * 10)) -mode-exit
|
||||||
|
sleep 1
|
||||||
|
free_mem=`cat "$meminfo" | grep MemFree:`
|
||||||
|
if [ "$exp_free_mem" != "$free_mem" ]; then
|
||||||
|
echo "NG: detected memory leak."
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
Reference in New Issue
Block a user