/****************************************************** /* /* waiter children outer_loop_size loop_size inner_loop_size sleep_time abort /* a simplified test case for linux load /* averages (way way way) too low issue. /* create children number of simultanious /* processes. Each process has a sleep_time /* (in microseconds). Tweek the computer dependent /* sleep frequency, overall execution time, reporting /* frequency, and duty cyle with various loop_size. /* abort = 0 to end program normally /* abort = 1 (or not zero) to abort upon completion /* of first child process. /* /* waiter.c Smythies 2013.04.08 /* Dramatically increase the maximum number of children /* that can be spun out, for a test. /* /* waiter.c Smythies 2012.06.10 /* The problem with the last version was that /* it also kills the calling script when it aborts. /* A better way is required. /* /* waiter.c Smythies 2012.05.24 /* This program is now often called from a higher /* level script accumulating long term reported /* load average data. Under heavy loads and /* several processes, a significant time stagger /* can develope between the child processes resulting /* in a significant actual load reduction towards the /* end of the program execution time. /* The result is an incorrect, low, actual load /* average veres what is predicted and expected. /* This edit will introduce a run time variable to /* instruct this program to either complete normally, or /* abort immediately when the first child process completes. /* This edit will also make the outer most loop a run time /* variable. /* /* waiter.c Smythies 2012.05.06 /* Add load print out, so that I don't have to /* calculate it all the time. /* Note: there is not I/O wait with this program, /* so don't get excited that it isn't included in /* the math. /* /* waiter.c Smythies 2012.05.04 /* Try to make the sleep duration a variable. /* It is desired to test a very low load, but /* with a high enter exit idle frequency. /* /* waiter.c Smythies 2012.02.13 /* Forgot to edit a hard coded number to the /* define. This occured several versions ago. /* I didn't notice, because I usually abort, /* rather than let it finish now. /* /* waiter.c Smythies 2012.02.11 /* Make the inner loop count a run time /* variable. /* I almost always run this for a long time /* now, and then abort it, so just leave the /* outer loop as a define. I.E. I don't care /* much about the end of run stats anymore. /* /* waiter.c Smythies 2012.02.07 /* (Stupid) Error in freq calculations. /* /* waiter.c Smythies 2012.02.04 /* Try to add sleep frequency information. /* The newer hardware does not have the same /* frequencies for issues as the two older /* hardwares. /* Not coded well (hack to see if it will work). /* /* waiter.c Smythies 2012.02.02 /* This program is becoming usefull. Start /* a proper history header. /* /* waiter.c Smythies 2012:02:01. From book page 85. Hence /* the strange program name. /* Linux Programming By Example - Que /* /********************************************************/ #include #include #include #include #include #include #include # #define LOAD_F 100 #define MAX_SLEEP_TIME 999999 int main(int argc, char **argv){ clock_t start, end, last, parent_start, parent_end; struct tms t_start, t_end, t_parent_start, t_parent_end; long tps; pid_t child[LOAD_F], has_ended; int status, retval, load_factor, outer_loop, inner_loop, inner_inner_loop, abort_prog; unsigned int sleep_time; int i,j,k,l,m,n; float parent_elapsed; switch(argc){ case 7: load_factor = atoi(argv[1]); outer_loop = atoi(argv[2]); inner_loop = atoi(argv[3]); inner_inner_loop = atoi(argv[4]); sleep_time = atoi(argv[5]); abort_prog = atoi(argv[6]); if(outer_loop < 1){ outer_loop = 1; printf("waiter: outer loop size must be at least 1. Clamped it.\n"); } /* endif */ if(load_factor > LOAD_F){ load_factor = LOAD_F; printf("waiter: load factor clamped at maximum, %3d.\n", load_factor); } else { if(load_factor < 1){ load_factor = 1; printf("waiter: load factor clamped at minimum, %3d.\n", load_factor); } /* endif */ } /* endif */ if(inner_loop < 1){ inner_loop = 1; printf("waiter: loop size must be at least 1. Clamped it.\n"); } /* endif */ if(inner_inner_loop < 1){ inner_inner_loop = 1; printf("waiter: inner loop size must be at least 1. Clamped it.\n"); } /* endif */ if(sleep_time > MAX_SLEEP_TIME){ sleep_time = MAX_SLEEP_TIME; printf("waiter: sleep time clamped at maximum, %7u microseconds.\n", sleep_time); } else { if(sleep_time < 1){ sleep_time = 1; printf("waiter: sleep time clamped at minimum, %7u microseconds.\n", sleep_time); } /* endif */ } /* endif */ break; default: printf("waiter load_factor outer_loop loop_size inner_loop_size sleep_time (in microseconds) abort\n"); printf(" run load_factor instances of the CPU time waster, waiter.\n"); printf(" use outer_loop_size to adjust the overall execution time for the program.\n"); printf(" use loop_size to adjust the sleep frequency, because it depends on the computer and the sleep time.\n"); printf(" use inner_loop_size to adjust the sleep to cpu duty cycle.\n"); printf(" if abort is 0, then the program completes normally, otherwise it aborts when the first child process completes.\n"); exit(-1); } /* endcase */ tps = sysconf(_SC_CLK_TCK); printf("waiter: ticks per second:%4ld\n", tps); parent_start = times(&t_parent_start); /* */ for(m = 0; m < load_factor; m++){ printf("waiter: process spawn loop%3d of%3d\n", m+1, load_factor); if((child[m] = fork()) == -1){ perror("fork"); exit(EXIT_FAILURE); } /* endif */ if(child[m] == 0){ start = times(&t_start); /* */ last = start; printf("\tchild %d pid = %d\n", m, getpid()); printf("\tchild %d ppid = %d\n", m, getppid()); for(i = 0; i < outer_loop; i++){ for(j = 0; j < inner_loop; j++){ for(k = 0; k < inner_inner_loop; k++){ l = i + j + k; } /* endfor */ usleep(sleep_time); } /* endfor */ end = times(&t_end); /* */ printf("Child%3d lp%5d of%5d: ", m, i + 1, outer_loop); printf("Elapsed:%7.2f s. ", ((float)(end - start) / (float)(tps))); /* */ printf("Delta:%7.2f s. ", ((float)(end - last) / (float)(tps))); /* */ printf("user cpu:%7.2f s. ", ((float)(t_end.tms_utime) / (float) (tps))); printf("sys cpu:%7.2f s. ", ((float)(t_end.tms_stime) / (float) (tps))); printf("load:%6.4f ", (float)(t_end.tms_stime + t_end.tms_utime) / (float)(end - start)); printf("last sleep freq.:%8.3f Hz. ", (float) inner_loop / ((float)(end - last) / (float)(tps))); printf("average sleep freq.:%8.3f Hz.\n", (float) inner_loop * (float ) (i + 1) / ((float)(end - start) / (float)(tps))); last = end; } /* endfor */ printf("Child%3d Total: ", m); printf("Elapsed:%7.2f s. ", ((float)(end - start) / (float)(tps))); /* */ printf("Delta:%9.4f s. (average)\n", ((float)(end - start) / (float) outer_loop / (float)(tps))); /* */ printf("\tchild %d pid = %d\n", m, getpid()); exit(EXIT_SUCCESS); } /* endif */ usleep(50000); } /* endfor */ puts("in parent .. about to start wait loops .."); printf("parent pid = %d : ", getpid()); printf("parent ppid = %d : ", getppid()); printf("children: "); for(m = 0; m < load_factor; m++){ printf("%d, ", (int) child[m]); } /* endfor */ printf("\n"); for(m = 0; m < load_factor; m++){ has_ended = waitpid(0, &status, 0); printf("in parent after wait %3d Status: %d Child %d has ended.\n", m, status, (int) has_ended); if(abort_prog !=0 && load_factor > 1 && m == 0){ /* abort if that is the mode of operation and more than one child. if m is not 0 something is wrong, just continue */ /* Is there a better, more elegant, way to do this? If it just exits, the children continue. */ /* If a global SIGKILL or SIGTERM is used then the calling script is killed or terminated also. */ printf("waiter: mode is abort after first child completes: Terminating my remaining children and then exiting...\n"); // retval = kill(0, SIGKILL); for(i = 0; i < load_factor; i++){ /* terminate the remaining children */ if(has_ended != child[i]){ /* check that it not the one that finished */ if((kill(child[i], SIGTERM)) < 0){ printf("Child %d did not terminate when asked. Issuing a global terminate...\n", (int) child[i]); retval = kill(0, SIGTERM); /* this should actually never be required */ } else { printf("Child %d terminated.\n", (int) child[i]); } /* endif */ } /* endif */ } /* endfor */ exit(EXIT_SUCCESS); /* terminate self */ } /* endif */ } /* endfor */ parent_end = times(&t_parent_end); /* */ parent_elapsed = ((float)(parent_end - parent_start) / (float)(tps)); printf("Parent: "); printf("Elapsed:%7.2f s. ", parent_elapsed); /* */ printf("Throughput:%9.4f s. (average) ", parent_elapsed / (float) outer_loop / (float) load_factor); /* */ // The children are not included in the parent, so the below are at or near 0.00 // printf("user cpu:%9.2f s. ", ((float)(t_parent_end.tms_utime) / (float) (tps))); // printf("sys cpu:%9.2f s.\n", ((float)(t_parent_end.tms_stime) / (float) (tps))); printf("Delta:%9.4f s. (average) \n", parent_elapsed / (float) outer_loop); /* */ exit(EXIT_SUCCESS); } /* endprogram */