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