/* 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--;
}