/* Download the .c file here */
#include <stdio.h> #include <sys/time.h> #include <stdlib.h> #include <unistd.h> #include <math.h> #include <signal.h> /* in bytes */ #define FILESIZE 4194304 #define CHUNK 512 #define SKIP 512 #define TESTNAME 'A' /**************************************/ /**************************************/ void set_rand_seed(void); unsigned int get_rand(int); void perrnexit(const char *); FILE* create_test_file(char); void unlink_test_file(char); void rand_mode(FILE *fd); void catch_alarm (int); void usage(const char *); int prepare(const long , const long, struct timespec *); void seq_mode(FILE *, off_t *); /* global countdown counter of reads/writes */ long tictocs; int main(int argc, char* argv[]) { // int i = 0; FILE * fd; long write_period = 0; long duration = 0; struct timespec ts; tictocs = 0; off_t cur_offset = 0; char mode = '\0'; if(argc == 4) { mode = argv[1][0]; write_period = (long) atoi(argv[2]); duration = (long) atoi(argv[3]); if(write_period < 0 || duration < 0) { usage(argv[0]); return -1; } } else { usage(argv[0]); return -1; } /* printf("arg0 %s -- %c \n",argv[1], mode ); * printf("arg0 %s -- %ld \n", argv[2], write_period ); * printf("arg0 %s -- %ld \n", argv[3], duration ); */ set_rand_seed(); /* choose a seed value */ /* set SIGALARM properly */ signal (SIGALRM, catch_alarm); if( prepare(write_period, duration, &ts) < 0) perrnexit("Clock does not support that high a resolution\n"); /* for multiple files test, create supports A-Z files */ fd = create_test_file(TESTNAME); while(1) { // printf("%ld\n", tictocs); if(tictocs <= 0) break; else{ switch(mode) { case 'R': case 'r': rand_mode(fd); break; case 'S': case 's': seq_mode(fd, &cur_offset); break; default: fclose(fd); unlink_test_file(TESTNAME); perrnexit("Unsupported mode"); } } nanosleep (&ts, NULL); } fclose(fd); unlink_test_file(TESTNAME); return 0; } /* If pages stay in page cache for long enough, despite the random * access some writings should overlap, so though they are issued * they are never applied to disk */ void rand_mode(FILE *fd) { int i; off_t offset = (off_t) get_rand(FILESIZE - CHUNK); fseek(fd, offset, SEEK_SET); for (i=0; i<CHUNK; i++) fputc (TESTNAME, fd ); rewind(fd); } /* If we access a file with a step of size sizeof(page) and * write a single byte every such chunk, provided pages * correspond to sequential blocks, some writes may be * coallesced. */ void seq_mode(FILE *fd, off_t *off) { int i; off_t offset = *off; if(offset + SKIP + CHUNK < FILESIZE) offset += SKIP; else offset = 0; *off = offset; fseek(fd, offset, SEEK_SET); for (i=0; i<CHUNK; i++) fputc (TESTNAME, fd ); *off = offset + CHUNK; return; } unsigned int get_rand(int upbound) { float tmp = 0 ; tmp = ( (double)rand() / ((double)(RAND_MAX)+(double)(1)) ); /* r is a random floating point value in the range [0,1) {including 0, not including 1}. Note we must convert rand() and/or RAND_MAX+1 to floating point values to avoid integer division. In addition, Sean Scanlon pointed out the possibility that RAND_MAX may be the largest positive integer the architecture can represent, so (RAND_MAX+1) may result in an overflow, or more likely the value will end up being the largest negative integer the architecture can represent, so to avoid this we convert RAND_MAX and 1 to doubles before adding. */ return (unsigned int) (tmp * upbound); } void set_rand_seed(void) { struct timeval tp; struct timezone tzp; if(! gettimeofday(&tp, &tzp)) srand((unsigned int) tp.tv_usec); else perrnexit("set_rand_seed"); return; } void perrnexit(const char* err) { printf("Unexpected error: %s. Exiting ...\n", err); exit(-1); } FILE* create_test_file(char f) { int i = 0; FILE *fd = NULL; char fname[7] = {' ', '.', 't', 'e', 's', 't', '\0'}; if (f >= 65 && f <= 90) fname[0] = f; else perrnexit("create_test_file wrong name"); /* if it previously exists, delete */ unlink(fname); /* open in rw mode */ if ( (fd = fopen(fname, "w+")) == NULL) perrnexit("create_test_file cannot open"); /* fill with nothing */ for (i=0; i<FILESIZE; i++) fputc ('\n', fd ); return fd; } void unlink_test_file(char c) { char fname[7] = {c, '.', 't', 'e', 's', 't', '\0'}; if (unlink(fname) != 0) perrnexit("cannot unlink, wrong name"); return; } void usage(const char *name) { fprintf(stderr, "\n****************************************"); fprintf(stderr, "***************************************\n"); fprintf(stderr, "\n%s Usage:\n\n", name); fprintf(stderr, "%s MODE (R|r: RANDOM, S|s:SEQUENTIAL) WRITES-PERIOD ", name); fprintf(stderr, "(usec) DURATION (SEC) \n"); fprintf(stderr, "\n**************************************"); fprintf(stderr, "*****************************************\n"); return ; } int prepare(long write_period_usec, const long duration_sec, struct timespec *slp) { long sec, usec; struct itimerval old, new, adj; double tmp; int r; if(write_period_usec <= 0 || duration_sec <= 0 ) perrnexit("invalid write_period/duration"); /* * printf("write %ld -- duration %ld \n", write_period_usec, duration_sec); */ /* global : number of writes to perform */ tmp = ((double) duration_sec) / ((double) (write_period_usec) * 0.000001) ; tictocs = (long) ceil(tmp); if(tictocs <= 0) perrnexit("invalid write_period/duration"); tmp = ((double) write_period_usec) / 1000000; if(tmp > duration_sec) perrnexit("Duration must be larger than write period"); /* * printf("tmp %f ---- %ld \n",tmp, write_period_usec); */ if(tmp >= 1) { sec = floor(tmp); usec = write_period_usec % 1000000; } else { sec = 0; usec = write_period_usec; } new.it_interval.tv_usec = usec; new.it_interval.tv_sec = sec; new.it_value.tv_usec = usec; new.it_value.tv_sec = sec; r = setitimer (ITIMER_REAL, &new, &old); /* re-read in case user asked for super high * resolution and write interval was reaggasted */ getitimer (ITIMER_REAL, &adj); slp->tv_sec = adj.it_interval.tv_sec; slp->tv_nsec = adj.it_interval.tv_usec * 1000; /* * printf("Write every %ld.%ld and sleep in between for %ld.%ld\n", adj.it_interval.tv_sec, adj.it_interval.tv_usec, lp->tv_sec, slp->tv_nsec ); */ return r; } void catch_alarm (int sig) { tictocs--; }