Home | History | Annotate | Download | only in mem_alloc_test
      1 /* ************************************************************************************
      2  *	mem_alloc.c
      3  *	@description : This program will consume memory using sbrk() to a size where
      4  *		       there is about COMMITED_AS KB left in free{swap+ram}.
      5  *		       The program realized that a process can consume so much memory,
      6  *		       space, so it will fork more child to consume as much as memory
      7  *		       possible, aiming for final free{swap+ram} < COMMITED_AS.
      8  *		       EXEPTION: If overcommit_momory is set, the program will only
      9  *			         consume as much as momory as oom-killer allows, and
     10  *				 will exit when then limit reached even the
     11  *				 free{swap+ram} not < COMMITTED_AS KB.
     12  *	@author	     : Sarunya Jimenez (sjimene (at) us.ibm.com)
     13  * ********************************************************************************** */
     14 
     15 /*
     16  * Copyright (C) 2003-2006 IBM
     17  *
     18  * This program is free software; you can redistribute it and/or
     19  * modify it under the terms of the GNU General Public License as
     20  * published by the Free Software Foundation; either version 2 of the
     21  * License, or (at your option) any later version.
     22  *
     23  * This program is distributed in the hope that it will be useful, but
     24  * WITHOUT ANY WARRANTY; without even the implied warranty of
     25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     26  * General Public License for more details.
     27  *
     28  * You should have received a copy of the GNU General Public License
     29  * along with this program; if not, write to the Free Software
     30  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
     31  * 02111-1307, USA.
     32  */
     33 
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <unistd.h>
     37 #include <signal.h>
     38 #include <sys/types.h>
     39 #include <sys/sysinfo.h>
     40 #include <sys/mman.h>
     41 #include <errno.h>
     42 
     43 /////////////////////////// GLOBAL STATIC VAIRABLE FOR SIGNAL HANDLER /////////////////
     44 static volatile sig_atomic_t sigflag;	// set nonzero by sig handler
     45 static sigset_t newmask, oldmask, zeromask;
     46 ////////////////////////////////////////////////////////////////////////////////////////
     47 
     48 //////////////////////////////// GLOBAL DEFINES ////////////////////////////////////////
     49 #define KB_VALUE        1024	// value in bytes -> 1024 bytes
     50 #define COMMITTED_AS  102400	// value in KB    -> 102400 KB -> 100MB
     51 #define MALLOC_SIZE   0x10000	// = 64KB... for each sbrk(MALLOC_SIZE) in
     52 					// malloc_data()
     53 					// MUST ALWAYS BE POSSITIVE VALUE
     54 #define PAGE_SIZE     0x400	// = 1024 KB
     55 /////////////////////////////////////////////////////////////////////////////////////////
     56 
     57 //////////////////////////////// GLOBAL VARIABLES ////////////////////////////////////////
     58 long sbrk_num;			// global sbrk_num to keep track of # of times sbrk() get called
     59 char *start_addr;		// heap @before a process allocate memory - get updated in eat_mem()
     60 char *end_addr;			// heap @after a process allocate memory  - get updated in alloc_data()
     61 			// and dealloc_data()
     62 //////////////////////////////////////////////////////////////////////////////////////////
     63 
     64 //////////////////////////////// ERROR HANDLING PRINT FUNCTIONS //////////////////////////
     65 /* ========================================================================================
     66  *	Print linux error message, will exit the current process.
     67  * ======================================================================================== */
     68 void unix_error(char *msg)
     69 {
     70 	printf("LINUX ERROR: %s: %s\n", msg, strerror(errno));
     71 	exit(0);
     72 }
     73 
     74 /* ========================================================================================
     75  *	Print functionality-error message for user process, will not exit the current process.
     76  * ======================================================================================== */
     77 void user_error(char *msg)
     78 {
     79 	printf("APPLICATION ERROR: %s\n", msg);
     80 }
     81 
     82 /////////////////////////////////////////////////////////////////////////////////////////////
     83 
     84 //////////////////////////// SIGNAL HANDLING FUNCTIONS ///////////////////////////////////////
     85 /* =====================================================================================
     86  *	One Signal Handler for SIGUSR1 and SIGUSR2.
     87  * ===================================================================================== */
     88 static void sig_usr(int signo)	// signal hanlder for SIGUSR1 and SIGUSR2
     89 {
     90 	sigflag = 1;
     91 }
     92 
     93 /* ========================================================================================
     94  *	SET UP signal handler before TELL_PARENT(), WAIT_PARENT(), TELL_CHILD(), WAIT_CHILD().
     95  *	- This function must be called before fork() and TELL/WAIT_PARENT/CHILD() functions.
     96  * ======================================================================================== */
     97 void TELL_WAIT(void)
     98 {
     99 	if (signal(SIGUSR1, sig_usr) == SIG_ERR)
    100 		unix_error("signal (SIGUSR1) FAILED");
    101 	if (signal(SIGUSR2, sig_usr) == SIG_ERR)
    102 		unix_error("signal (SIGUSR2) FAILED");
    103 
    104 	sigemptyset(&zeromask);
    105 	sigemptyset(&newmask);
    106 	sigaddset(&newmask, SIGUSR1);
    107 	sigaddset(&newmask, SIGUSR2);
    108 
    109 	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
    110 		unix_error("signal (SIG_BLOCK) FAILED");
    111 }
    112 
    113 /* ========================================================================================
    114  *	TELL parent that we are done: used in child process.
    115  *	- This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery.
    116  *	- INPUT: parent process ID; can be obtained through getppid().
    117  * ======================================================================================== */
    118 void TELL_PARENT(pid_t pid)
    119 {
    120 	kill(pid, SIGUSR2);	// send signal SIGUSR2 to pid process
    121 }
    122 
    123 /* ========================================================================================
    124  *	TELL child that we are done: used in parent process.
    125  *	- This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery.
    126  *	- INPUT: child process ID; can be obtained through pid = fork() where pid > 0.
    127  * ======================================================================================== */
    128 void TELL_CHILD(pid_t pid)
    129 {
    130 	kill(pid, SIGUSR1);	// send signal SIGUSR1 to pid process
    131 }
    132 
    133 /* ========================================================================================
    134  *	WAIT for parent: used in child process.
    135  *	- This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery.
    136  * ======================================================================================== */
    137 void WAIT_PARENT(void)
    138 {
    139 	while (sigflag == 0)
    140 		sigsuspend(&zeromask);	// wait for child
    141 	sigflag = 0;
    142 
    143 	// reset signal mask to original value
    144 	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    145 		unix_error("signal (SIG_SETMASK) FAILED");
    146 }
    147 
    148 /* ========================================================================================
    149  *	WAIT for child: used in parent process.
    150  *	- This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery.
    151  * ======================================================================================== */
    152 void WAIT_CHILD(void)
    153 {
    154 	while (sigflag == 0)
    155 		sigsuspend(&zeromask);	// wait for parent
    156 	sigflag = 0;
    157 
    158 	// reset signal mask to original value
    159 	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    160 		unix_error("signal (SIG_SETMASK) FAILED");
    161 }
    162 
    163 /////////////////////////////////////////////////////////////////////////////////////////////
    164 
    165 /////////////////////////////////////// MEMORY ALLOCATION FUNCTIONS /////////////////////////
    166 /* =====================================================================================
    167  *	SET sbrk_num @start of each process to count # of sbrk() calls within that process.
    168  *	- INPUT: input number for globak sbrk_num to be set to.
    169  * ===================================================================================== */
    170 void set_sbrk_num(int in)
    171 {
    172 	sbrk_num = in;
    173 }
    174 
    175 /* ========================================================================================
    176  *	PRINT system information; e.g. free {ram, swap}, total {ram, swap}.
    177  * ======================================================================================== */
    178 void print_sysinfo(void)
    179 {
    180 	struct sysinfo si;
    181 	sysinfo(&si);
    182 
    183 	printf
    184 	    ("freeram (%luKB),  freeswap (%luKB), totalram (%luKB), totalswap (%luKB)\n",
    185 	     (si.freeram / KB_VALUE) * si.mem_unit,
    186 	     (si.freeswap / KB_VALUE) * si.mem_unit,
    187 	     (si.totalram / KB_VALUE) * si.mem_unit,
    188 	     (si.totalswap / KB_VALUE) * si.mem_unit);
    189 }
    190 
    191 /* ========================================================================================
    192  *	CALCULATE freeswap space.
    193  *	- OUTPUT: Return size of free swap space in KB.
    194  * ======================================================================================== */
    195 long unsigned freeswap(void)
    196 {
    197 	struct sysinfo si;
    198 	sysinfo(&si);
    199 
    200 	return ((si.freeswap / KB_VALUE) * si.mem_unit);
    201 }
    202 
    203 /* ========================================================================================
    204  *	CALCULATE freeram space.
    205  *	- OUTPUT: Return size of free ram space in KB.
    206  * ======================================================================================== */
    207 long unsigned freeram(void)
    208 {
    209 	struct sysinfo si;
    210 	sysinfo(&si);
    211 
    212 	return ((si.freeram / KB_VALUE) * si.mem_unit);
    213 }
    214 
    215 /* ========================================================================================
    216  *	ALLOCATE data using sbrk(incr).
    217  *  	- Global sbrk_num will be updated for each time calling sbrk() to increase heap size.
    218  *	- OUTPUT: Return 1 if success,
    219  *			 0 if failed, and will decrement the heap space for future library calls
    220  * ======================================================================================== */
    221 int malloc_data(void)
    222 {
    223 	int return_value = 0;	// default return = true;
    224 	intptr_t incr = MALLOC_SIZE;	// 64KB
    225 	char *src = NULL;	// to hold addr return from sbrk(incr)
    226 	long i;			// loop counter
    227 
    228 	src = sbrk(incr);
    229 
    230 	if (((void *)src == (void *)-1) && (errno == ENOMEM)) {	// error handling
    231 		src = sbrk(-(2 * incr));	// freeing some space for later library calls
    232 		sbrk_num -= 2;
    233 		end_addr = src + (-(2 * incr));	// update end of heap
    234 	} else {		// sucess case
    235 		// must write to data, write once for each 1KB
    236 		for (i = 0x0; i < incr; i += PAGE_SIZE)
    237 			src[i] = '*';
    238 		++sbrk_num;	// update global sbrk() call counter when success
    239 		return_value = 1;	// update return value to true
    240 		end_addr = src + incr;	// update end of heap
    241 	}
    242 
    243 	return return_value;
    244 }
    245 
    246 /* ========================================================================================
    247  *	DEALLOCATE data using sbrk(-incr).
    248  *  	- Global sbrk_num will be updated for each time calling sbrk() to decrease heap size.
    249  *	- OUTPUT: Return 1 if success,
    250  *			 0 if failed.
    251  * ======================================================================================== */
    252 int dealloc_data(void)
    253 {
    254 	int return_value = 0;	// default return = true
    255 	intptr_t incr = MALLOC_SIZE;	// 64KB
    256 	char *src = NULL;	// to hold adrr return from sbrk(incr)
    257 	long i;			// loop counter
    258 	long old_sbrk_num = sbrk_num;	// save old sbrk_num counter, because sbrk_num will be updated
    259 
    260 	for (i = 0; i < old_sbrk_num; ++i) {
    261 		src = sbrk(-incr);
    262 
    263 		// error handling: Fatal Fail
    264 		if (((void *)src == (void *)-1) && (errno == ENOMEM))
    265 			goto OUT;	// error
    266 
    267 		--sbrk_num;	// update # of sbrk() call
    268 		end_addr = src + (-incr);	// update end of heap
    269 	}
    270 	return_value = 1;	// update return value to true
    271 
    272 OUT:
    273 	return return_value;
    274 }
    275 
    276 /* ========================================================================================
    277  *	Write to the memory because of Copy-On-Write behavior from LINUX kernel.
    278  *	IDEA: Because fork() is implemented through Copy-On-Write. This technique
    279  *	      delay/prevent the copy of data, child & parent share memory, and their
    280  *	      duplication of the address of child & parent are shared read-only. For parent
    281  *	      & child to have its very own separate space, both must write to their own data.
    282  *	      So this function will deal with the write for the child process created
    283  *	      by fork().
    284  *	OUTPUT: Return 1 if success,
    285  *		       0 if failed.
    286  * ======================================================================================== */
    287 int handle_COW(void)
    288 {
    289 	int return_value = 0;	// default return = true
    290 	intptr_t incr = MALLOC_SIZE;	// 64KB
    291 	char *src = NULL;	// to hold adrr return from sbrk(incr)
    292 	char *i;		// loop counter
    293 
    294 	// error handling: Make sure the start_addr is not NULL
    295 	if (start_addr == NULL) {
    296 		user_error("start_addr from parent is not initialized");
    297 		goto OUT;
    298 	}
    299 	// error handling: Make sure the end_addr is not NULL
    300 	if (end_addr == NULL) {
    301 		user_error("end_addr from parent is not initialized");
    302 		goto OUT;
    303 	}
    304 	// Writing to heap
    305 	if (start_addr < end_addr) {	// Heap grows up to higher address
    306 		for (i = start_addr; i < end_addr; i += PAGE_SIZE) {
    307 			if ((freeswap() + freeram()) < COMMITTED_AS)
    308 				goto OUT;
    309 			*i = 'u';
    310 		}
    311 		return_value = 1;
    312 	} else if (start_addr > end_addr) {	// Heap grows down to lower address
    313 		for (i = end_addr; i > start_addr; i -= PAGE_SIZE) {
    314 			if ((freeswap() + freeram()) < COMMITTED_AS)
    315 				goto OUT;
    316 			*i = 'd';
    317 		}
    318 		return_value = 1;
    319 	} else;			// Heap doesn't grows
    320 
    321 OUT:
    322 	return return_value;
    323 }
    324 
    325 /* ========================================================================================
    326  *	EAT lots and lots of memory...
    327  *	- If a process can eat all of the free resouces
    328  *	  specified, that process will exit the program.
    329  * ======================================================================================== */
    330 void eat_mem(void)
    331 {
    332 	// saving the current heap pointer befoer start to allocate more memory
    333 	start_addr = NULL;
    334 	end_addr = NULL;
    335 	start_addr = sbrk(0);
    336 
    337 	// eating memory
    338 	while ((freeswap() + freeram()) > COMMITTED_AS) {
    339 		if (!malloc_data())
    340 			return;
    341 	}
    342 
    343 	print_sysinfo();
    344 	exit(0);
    345 }
    346 
    347 /* ========================================================================================
    348  *	EAT lots and lots of memory...If a process can eat all of the free resouces
    349  *	specified, that process will exit the program
    350  * ======================================================================================== */
    351 void eat_mem_no_exit(void)
    352 {
    353 	// saving the current heap pointer befoer start to allocate more memory
    354 	start_addr = NULL;
    355 	end_addr = NULL;
    356 	start_addr = sbrk(0);
    357 
    358 	// eating memory
    359 	while ((freeswap() + freeram()) > COMMITTED_AS) {
    360 		if (!malloc_data())
    361 			break;
    362 	}
    363 }
    364 
    365 ///////////////////////////////////////////////////////////////////////////////////////////////////
    366 
    367 ///////////////////////////////// MAIN PROGRAM ////////////////////////////////////////////////////
    368 int main(int argc, char **argv)
    369 {
    370 	pid_t pid;		// used for fork()
    371 	print_sysinfo();	// sytem resouces before start allocation
    372 	set_sbrk_num(0);	// at start of process, ensure sbrk_num is set
    373 	eat_mem();
    374 
    375 	// @beyound this point -> 1 process can't consume all memory so it must fork a child to consume more
    376 	// memory
    377 START:
    378 	pid = fork();
    379 	pid = pid < 0 ? -1 : pid;
    380 
    381 	switch (pid) {
    382 	case -1:
    383 		if (!dealloc_data())
    384 			unix_error
    385 			    ("SBRK(-incr) FROM DEALLOC_DATA() FAILED. FATAL!!!");
    386 		goto LAST_CONDITION;
    387 
    388 	case 0:
    389 		if (!handle_COW()) {	// Re-touch child pages
    390 			print_sysinfo();	// FINAL RESULT, LAST RESOURCES
    391 			exit(0);	// child can't allocate no more, DONE!!!
    392 		}
    393 		goto START;
    394 
    395 	default:
    396 		if (waitpid(-1, NULL, 0) != pid)	// Parent Waiting
    397 			unix_error("WAIT_PID FAILED. FATAL!!!");
    398 		exit(0);
    399 	}
    400 
    401 LAST_CONDITION:
    402 	TELL_WAIT();		// set up parent/child signal handler
    403 	pid = fork();
    404 	pid = pid < 0 ? -1 : pid;
    405 
    406 	switch (pid) {
    407 	case -1:
    408 		unix_error("FORK FAILED.");
    409 
    410 	case 0:
    411 		eat_mem_no_exit();
    412 		WAIT_PARENT();
    413 		print_sysinfo();	// FINAL RESULT, LAST RESOUCES
    414 		TELL_PARENT(getppid());
    415 		exit(0);
    416 
    417 	default:
    418 		eat_mem_no_exit();
    419 		TELL_CHILD(pid);
    420 		WAIT_CHILD();
    421 		exit(0);
    422 	}
    423 }
    424 
    425 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
    426