• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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  * ======================================================================================== */
unix_error(char * msg)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  * ======================================================================================== */
user_error(char * msg)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  * ===================================================================================== */
sig_usr(int signo)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  * ======================================================================================== */
TELL_WAIT(void)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  * ======================================================================================== */
TELL_PARENT(pid_t pid)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  * ======================================================================================== */
TELL_CHILD(pid_t pid)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  * ======================================================================================== */
WAIT_PARENT(void)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  * ======================================================================================== */
WAIT_CHILD(void)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  * ===================================================================================== */
set_sbrk_num(int in)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  * ======================================================================================== */
print_sysinfo(void)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  * ======================================================================================== */
freeswap(void)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  * ======================================================================================== */
freeram(void)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  * ======================================================================================== */
malloc_data(void)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  * ======================================================================================== */
dealloc_data(void)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  * ======================================================================================== */
handle_COW(void)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  * ======================================================================================== */
eat_mem(void)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  * ======================================================================================== */
eat_mem_no_exit(void)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 ////////////////////////////////////////////////////
main(int argc,char ** argv)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