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