| /* ************************************************************************************ |
| * mem_alloc.c |
| * @description : This program will consume memory using sbrk() to a size where |
| * there is about COMMITED_AS KB left in free{swap+ram}. |
| * The program realized that a process can consume so much memory, |
| * space, so it will fork more child to consume as much as memory |
| * possible, aiming for final free{swap+ram} < COMMITED_AS. |
| * EXEPTION: If overcommit_momory is set, the program will only |
| * consume as much as momory as oom-killer allows, and |
| * will exit when then limit reached even the |
| * free{swap+ram} not < COMMITTED_AS KB. |
| * @author : Sarunya Jimenez (sjimene@us.ibm.com) |
| * ********************************************************************************** */ |
| |
| /* |
| * Copyright (C) 2003-2006 IBM |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/sysinfo.h> |
| #include <sys/mman.h> |
| #include <errno.h> |
| |
| /////////////////////////// GLOBAL STATIC VAIRABLE FOR SIGNAL HANDLER ///////////////// |
| static volatile sig_atomic_t sigflag; // set nonzero by sig handler |
| static sigset_t newmask, oldmask, zeromask; |
| //////////////////////////////////////////////////////////////////////////////////////// |
| |
| //////////////////////////////// GLOBAL DEFINES //////////////////////////////////////// |
| #define KB_VALUE 1024 // value in bytes -> 1024 bytes |
| #define COMMITTED_AS 102400 // value in KB -> 102400 KB -> 100MB |
| #define MALLOC_SIZE 0x10000 // = 64KB... for each sbrk(MALLOC_SIZE) in |
| // malloc_data() |
| // MUST ALWAYS BE POSSITIVE VALUE |
| #define PAGE_SIZE 0x400 // = 1024 KB |
| ///////////////////////////////////////////////////////////////////////////////////////// |
| |
| //////////////////////////////// GLOBAL VARIABLES //////////////////////////////////////// |
| long sbrk_num; // global sbrk_num to keep track of # of times sbrk() get called |
| char *start_addr; // heap @before a process allocate memory - get updated in eat_mem() |
| char *end_addr; // heap @after a process allocate memory - get updated in alloc_data() |
| // and dealloc_data() |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| //////////////////////////////// ERROR HANDLING PRINT FUNCTIONS ////////////////////////// |
| /* ======================================================================================== |
| * Print linux error message, will exit the current process. |
| * ======================================================================================== */ |
| void unix_error(char *msg) |
| { |
| printf("LINUX ERROR: %s: %s\n", msg, strerror(errno)); |
| exit(0); |
| } |
| |
| /* ======================================================================================== |
| * Print functionality-error message for user process, will not exit the current process. |
| * ======================================================================================== */ |
| void user_error(char *msg) |
| { |
| printf("APPLICATION ERROR: %s\n", msg); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////////////////////// |
| |
| //////////////////////////// SIGNAL HANDLING FUNCTIONS /////////////////////////////////////// |
| /* ===================================================================================== |
| * One Signal Handler for SIGUSR1 and SIGUSR2. |
| * ===================================================================================== */ |
| static void sig_usr(int signo) // signal hanlder for SIGUSR1 and SIGUSR2 |
| { |
| sigflag = 1; |
| } |
| |
| /* ======================================================================================== |
| * SET UP signal handler before TELL_PARENT(), WAIT_PARENT(), TELL_CHILD(), WAIT_CHILD(). |
| * - This function must be called before fork() and TELL/WAIT_PARENT/CHILD() functions. |
| * ======================================================================================== */ |
| void TELL_WAIT(void) |
| { |
| if (signal(SIGUSR1, sig_usr) == SIG_ERR) |
| unix_error("signal (SIGUSR1) FAILED"); |
| if (signal(SIGUSR2, sig_usr) == SIG_ERR) |
| unix_error("signal (SIGUSR2) FAILED"); |
| |
| sigemptyset(&zeromask); |
| sigemptyset(&newmask); |
| sigaddset(&newmask, SIGUSR1); |
| sigaddset(&newmask, SIGUSR2); |
| |
| if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) |
| unix_error("signal (SIG_BLOCK) FAILED"); |
| } |
| |
| /* ======================================================================================== |
| * TELL parent that we are done: used in child process. |
| * - This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery. |
| * - INPUT: parent process ID; can be obtained through getppid(). |
| * ======================================================================================== */ |
| void TELL_PARENT(pid_t pid) |
| { |
| kill(pid, SIGUSR2); // send signal SIGUSR2 to pid process |
| } |
| |
| /* ======================================================================================== |
| * TELL child that we are done: used in parent process. |
| * - This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery. |
| * - INPUT: child process ID; can be obtained through pid = fork() where pid > 0. |
| * ======================================================================================== */ |
| void TELL_CHILD(pid_t pid) |
| { |
| kill(pid, SIGUSR1); // send signal SIGUSR1 to pid process |
| } |
| |
| /* ======================================================================================== |
| * WAIT for parent: used in child process. |
| * - This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery. |
| * ======================================================================================== */ |
| void WAIT_PARENT(void) |
| { |
| while (sigflag == 0) |
| sigsuspend(&zeromask); // wait for child |
| sigflag = 0; |
| |
| // reset signal mask to original value |
| if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) |
| unix_error("signal (SIG_SETMASK) FAILED"); |
| } |
| |
| /* ======================================================================================== |
| * WAIT for child: used in parent process. |
| * - This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery. |
| * ======================================================================================== */ |
| void WAIT_CHILD(void) |
| { |
| while (sigflag == 0) |
| sigsuspend(&zeromask); // wait for parent |
| sigflag = 0; |
| |
| // reset signal mask to original value |
| if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) |
| unix_error("signal (SIG_SETMASK) FAILED"); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /////////////////////////////////////// MEMORY ALLOCATION FUNCTIONS ///////////////////////// |
| /* ===================================================================================== |
| * SET sbrk_num @start of each process to count # of sbrk() calls within that process. |
| * - INPUT: input number for globak sbrk_num to be set to. |
| * ===================================================================================== */ |
| void set_sbrk_num(int in) |
| { |
| sbrk_num = in; |
| } |
| |
| /* ======================================================================================== |
| * PRINT system information; e.g. free {ram, swap}, total {ram, swap}. |
| * ======================================================================================== */ |
| void print_sysinfo(void) |
| { |
| struct sysinfo si; |
| sysinfo(&si); |
| |
| printf |
| ("freeram (%luKB), freeswap (%luKB), totalram (%luKB), totalswap (%luKB)\n", |
| (si.freeram / KB_VALUE) * si.mem_unit, |
| (si.freeswap / KB_VALUE) * si.mem_unit, |
| (si.totalram / KB_VALUE) * si.mem_unit, |
| (si.totalswap / KB_VALUE) * si.mem_unit); |
| } |
| |
| /* ======================================================================================== |
| * CALCULATE freeswap space. |
| * - OUTPUT: Return size of free swap space in KB. |
| * ======================================================================================== */ |
| long unsigned freeswap(void) |
| { |
| struct sysinfo si; |
| sysinfo(&si); |
| |
| return ((si.freeswap / KB_VALUE) * si.mem_unit); |
| } |
| |
| /* ======================================================================================== |
| * CALCULATE freeram space. |
| * - OUTPUT: Return size of free ram space in KB. |
| * ======================================================================================== */ |
| long unsigned freeram(void) |
| { |
| struct sysinfo si; |
| sysinfo(&si); |
| |
| return ((si.freeram / KB_VALUE) * si.mem_unit); |
| } |
| |
| /* ======================================================================================== |
| * ALLOCATE data using sbrk(incr). |
| * - Global sbrk_num will be updated for each time calling sbrk() to increase heap size. |
| * - OUTPUT: Return 1 if success, |
| * 0 if failed, and will decrement the heap space for future library calls |
| * ======================================================================================== */ |
| int malloc_data(void) |
| { |
| int return_value = 0; // default return = true; |
| intptr_t incr = MALLOC_SIZE; // 64KB |
| char *src = NULL; // to hold addr return from sbrk(incr) |
| long i; // loop counter |
| |
| src = sbrk(incr); |
| |
| if (((void *)src == (void *)-1) && (errno == ENOMEM)) { // error handling |
| src = sbrk(-(2 * incr)); // freeing some space for later library calls |
| sbrk_num -= 2; |
| end_addr = src + (-(2 * incr)); // update end of heap |
| } else { // sucess case |
| // must write to data, write once for each 1KB |
| for (i = 0x0; i < incr; i += PAGE_SIZE) |
| src[i] = '*'; |
| ++sbrk_num; // update global sbrk() call counter when success |
| return_value = 1; // update return value to true |
| end_addr = src + incr; // update end of heap |
| } |
| |
| return return_value; |
| } |
| |
| /* ======================================================================================== |
| * DEALLOCATE data using sbrk(-incr). |
| * - Global sbrk_num will be updated for each time calling sbrk() to decrease heap size. |
| * - OUTPUT: Return 1 if success, |
| * 0 if failed. |
| * ======================================================================================== */ |
| int dealloc_data(void) |
| { |
| int return_value = 0; // default return = true |
| intptr_t incr = MALLOC_SIZE; // 64KB |
| char *src = NULL; // to hold adrr return from sbrk(incr) |
| long i; // loop counter |
| long old_sbrk_num = sbrk_num; // save old sbrk_num counter, because sbrk_num will be updated |
| |
| for (i = 0; i < old_sbrk_num; ++i) { |
| src = sbrk(-incr); |
| |
| // error handling: Fatal Fail |
| if (((void *)src == (void *)-1) && (errno == ENOMEM)) |
| goto OUT; // error |
| |
| --sbrk_num; // update # of sbrk() call |
| end_addr = src + (-incr); // update end of heap |
| } |
| return_value = 1; // update return value to true |
| |
| OUT: |
| return return_value; |
| } |
| |
| /* ======================================================================================== |
| * Write to the memory because of Copy-On-Write behavior from LINUX kernel. |
| * IDEA: Because fork() is implemented through Copy-On-Write. This technique |
| * delay/prevent the copy of data, child & parent share memory, and their |
| * duplication of the address of child & parent are shared read-only. For parent |
| * & child to have its very own separate space, both must write to their own data. |
| * So this function will deal with the write for the child process created |
| * by fork(). |
| * OUTPUT: Return 1 if success, |
| * 0 if failed. |
| * ======================================================================================== */ |
| int handle_COW(void) |
| { |
| int return_value = 0; // default return = true |
| intptr_t incr = MALLOC_SIZE; // 64KB |
| char *src = NULL; // to hold adrr return from sbrk(incr) |
| char *i; // loop counter |
| |
| // error handling: Make sure the start_addr is not NULL |
| if (start_addr == NULL) { |
| user_error("start_addr from parent is not initialized"); |
| goto OUT; |
| } |
| // error handling: Make sure the end_addr is not NULL |
| if (end_addr == NULL) { |
| user_error("end_addr from parent is not initialized"); |
| goto OUT; |
| } |
| // Writing to heap |
| if (start_addr < end_addr) { // Heap grows up to higher address |
| for (i = start_addr; i < end_addr; i += PAGE_SIZE) { |
| if ((freeswap() + freeram()) < COMMITTED_AS) |
| goto OUT; |
| *i = 'u'; |
| } |
| return_value = 1; |
| } else if (start_addr > end_addr) { // Heap grows down to lower address |
| for (i = end_addr; i > start_addr; i -= PAGE_SIZE) { |
| if ((freeswap() + freeram()) < COMMITTED_AS) |
| goto OUT; |
| *i = 'd'; |
| } |
| return_value = 1; |
| } else; // Heap doesn't grows |
| |
| OUT: |
| return return_value; |
| } |
| |
| /* ======================================================================================== |
| * EAT lots and lots of memory... |
| * - If a process can eat all of the free resouces |
| * specified, that process will exit the program. |
| * ======================================================================================== */ |
| void eat_mem(void) |
| { |
| // saving the current heap pointer befoer start to allocate more memory |
| start_addr = NULL; |
| end_addr = NULL; |
| start_addr = sbrk(0); |
| |
| // eating memory |
| while ((freeswap() + freeram()) > COMMITTED_AS) { |
| if (!malloc_data()) |
| return; |
| } |
| |
| print_sysinfo(); |
| exit(0); |
| } |
| |
| /* ======================================================================================== |
| * EAT lots and lots of memory...If a process can eat all of the free resouces |
| * specified, that process will exit the program |
| * ======================================================================================== */ |
| void eat_mem_no_exit(void) |
| { |
| // saving the current heap pointer befoer start to allocate more memory |
| start_addr = NULL; |
| end_addr = NULL; |
| start_addr = sbrk(0); |
| |
| // eating memory |
| while ((freeswap() + freeram()) > COMMITTED_AS) { |
| if (!malloc_data()) |
| break; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| ///////////////////////////////// MAIN PROGRAM //////////////////////////////////////////////////// |
| int main(int argc, char **argv) |
| { |
| pid_t pid; // used for fork() |
| print_sysinfo(); // sytem resouces before start allocation |
| set_sbrk_num(0); // at start of process, ensure sbrk_num is set |
| eat_mem(); |
| |
| // @beyound this point -> 1 process can't consume all memory so it must fork a child to consume more |
| // memory |
| START: |
| pid = fork(); |
| pid = pid < 0 ? -1 : pid; |
| |
| switch (pid) { |
| case -1: |
| if (!dealloc_data()) |
| unix_error |
| ("SBRK(-incr) FROM DEALLOC_DATA() FAILED. FATAL!!!"); |
| goto LAST_CONDITION; |
| |
| case 0: |
| if (!handle_COW()) { // Re-touch child pages |
| print_sysinfo(); // FINAL RESULT, LAST RESOURCES |
| exit(0); // child can't allocate no more, DONE!!! |
| } |
| goto START; |
| |
| default: |
| if (waitpid(-1, NULL, 0) != pid) // Parent Waiting |
| unix_error("WAIT_PID FAILED. FATAL!!!"); |
| exit(0); |
| } |
| |
| LAST_CONDITION: |
| TELL_WAIT(); // set up parent/child signal handler |
| pid = fork(); |
| pid = pid < 0 ? -1 : pid; |
| |
| switch (pid) { |
| case -1: |
| unix_error("FORK FAILED."); |
| |
| case 0: |
| eat_mem_no_exit(); |
| WAIT_PARENT(); |
| print_sysinfo(); // FINAL RESULT, LAST RESOUCES |
| TELL_PARENT(getppid()); |
| exit(0); |
| |
| default: |
| eat_mem_no_exit(); |
| TELL_CHILD(pid); |
| WAIT_CHILD(); |
| exit(0); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////////////////// |