home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume22
/
byte-benchmarks
/
part05
/
dbmscli.c
Wrap
C/C++ Source or Header
|
1990-06-07
|
20KB
|
827 lines
/*******************************************************************************
* The BYTE UNIX Benchmarks - Release 2
* Module: dbmscli.c SID: 2.4 4/17/90 16:45:36
*
*******************************************************************************
* Bug reports, patches, comments, suggestions should be sent to:
*
* Ben Smith or Rick Grehan at BYTE Magazine
* bensmith@bixpb.UUCP rick_g@bixpb.UUCP
*
*****************************************************************************
* Modification Log:
*
* 7/6/89 - Reworked messaging system so the semaphores were no
* longer used in all cases. Semaphores now only control the append
* operation. Messages contain caller's pid in type field.
* The semaphore also serves to indicate the presence of the server. RG
*
* 7/6/89 - added some debugging output to VERBOSE
* 7/9/89 - ifdef code that starts server from client - problems. BSS
* 7/11/89 - Added semaphore to set. One semaphore controls the queue,
* the other controls the append operation. RG
* New command line format: dbmserv <filename> <queue size>
*****************************************************************************/
/*
* Multi-user DBMS simulation.
* Clients
* Database has the form:
* IIIINNNNNNNNNNNNNNNNNNNNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPPPPPPPP
* Where IIII is the 4-digit id number (0-padded)
* NN... is the 20-character name
* AA... is the 40-character address
* PP... is the 10-character phone number
* Records are accessed by ID number (1 is the 0th record, 2 is 1st..etc.)
*
* This is the client system, which you execute with:
* dbmscli n t i
* where:
* n = the number of tasks to fork
* t = is the sleep time in seconds each task waits between requests
* i = is the number of test iterations
*
* NOTE: This version assumes Unix V compatibility.
*/
char id[] = "@(#) @(#)dbmscli.c:1.5 -- 7/10/89 18:54:57";
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <sys/param.h>
#ifdef VERBOSE
#include <sys/times.h>
#define DEBUG /* remove this after debugging */
#endif
#define ERROR (-1)
/*
** Record definitions.
*/
#define IDLEN 4
#define NAMELEN 20
#define ADDRLEN 40
#define PHONLEN 10
#define RECLEN 74 /* Sum of the above. */
/*
** Queue and semaphore names.
*/
#define RQUEUE "READ" /* Read queue */
#define WQUEUE "WRIT" /* Write queue */
#define SEMA "SEMA" /* Semaphore name */
/*
** Message types.
*/
#define READREQ 1 /* Read a record */
#define WRITEREQ 2 /* Write a record */
#define ADDREQ 3 /* Add a new record */
#define GETLREQ 4 /* Get largest record number */
#define RESULTREQ 10 /* Record contains results figures */
/* Results are stored as:
* nnnnnnnnnnbmmmmmmmmmmb
* n = total time
* m = number of errors
* b = blank
*/
#define DIEREQ 99 /* Orders server to terminate. */
/*
** Return codes from the server.
*/
#define AOK 1 /* Request met ok */
#define DERR_RNF 2 /* Record not found */
#define DERR_RAE 3 /* Record already exists */
#define DERR_WRD 4 /* Unexplainable error */
#define DERR_UNK 9 /* Unknown request type */
/*
** Error codes.
*/
#define QERR 1 /* Queue error */
#define SEMERR 2 /* Semaphore error */
/*
** Structures.
*/
typedef struct {
char id[IDLEN];
char name[NAMELEN];
char address[ADDRLEN];
char phone[PHONLEN];
} dbrec;
typedef struct {
int request; /* Request type and response code */
char recdat[RECLEN]; /* DBMS record data */
} msgdata;
typedef struct {
long type; /* Hold's caller's pid */
msgdata data; /* Pointer to request and data */
} amess;
struct ticker { unsigned long real,
system,
cpu; };
/******************************************************************
#### # #### ##### ## # ####
# # # # # # # # # # #
# # # # ##### # # # ####
# ### # # # # # ###### # #
# # # # # # # # # # # #
#### ###### #### ##### # # ###### ####
*******************************************************************/
/*
** Structure instances.
*/
dbrec myrec; /* Data record */
amess myreq; /* Client request */
amess hisresp; /* Response from server */
struct sembuf unlockq = { 0 , 1 , SEM_UNDO };
struct sembuf lockq = { 0 , -1 , SEM_UNDO };
struct sembuf unlocka = { 0, 1, SEM_UNDO };
struct sembuf locka = { 0, -1, SEM_UNDO };
#ifdef VERBOSE
struct tms tbuff; /* For times system call */
struct ticker stopwatch = {0L, 0L, 0L};
struct ticker tottime = {0L, 0L, 0L};
#endif
int ntasks = 1; /* Number of tasks */
int sleeptime = 0; /* Time to sleep */
int iters = 1000; /* iterations */
int readq; /* ID of read queue */
int writeq; /* ID of write queue */
int qsema; /* ID of semaphore for append */
unsigned long errcnt; /* Error count */
int waitval; /* Status return location for wait() */
int rid; /* ID chosen at random */
int need_server; /* flag for server */
key_t sema_key;
/*
** Externs and function defs.
*/
long randnum(), randwc();
key_t makey();
/**************************************************************************
# # ## # # #
## ## # # # ## #
# ## # # # # # # #
# # ###### # # # #
# # # # # # ##
# # # # # # #
****************************************************************************/
main(argc,argv,envp)
int argc;
char *argv[];
char **envp;
{
int i,j,cerr; /* Loop variables */
/*
* Make sure we have proper input.
*/
if(argc<2)
{
fprintf(stderr,
"usage: %s datafile [ntasks] [sleeptime] [iter]\n",argv[0]);
exit(1);
}
/************* process command line parameters ************/
/*
* Get number of tasks and sleeptime.
*/
if(argc==5)
{
if((iters=atoi(argv[4]))<=0)
{
fprintf(stderr,"**Illegal iter\n");
exit(1);
}
}
if(argc >=4)
{
if((sleeptime=atoi(argv[3]))<0)
{
fprintf(stderr,"**Illegal sleep time\n");
exit(1);
}
}
if(argc >=3)
{
if((ntasks=atoi(argv[2]))<=0)
{
fprintf(stderr,"**Illegal ntasks\n");
exit(1);
}
}
/* argv[1] is the data file name */
/*
* Make sure the server is active.
*/
sema_key=makey(SEMA);
/* test to see if the server is already running */
if((qsema=semget(sema_key,2,0))==ERROR)
{
int bad_news();
/* fork off for an exec to the server */
if(!(j=fork())) /* this is the child aspiring to be */
{ /* a server */
#ifdef DEBUG
printf("I am the child (%d) aspiring to be a server\nn",
getpid());
#endif
argv[0]="dbmserv";
execvp(argv[0],argv);
/* if it gets this far, exec must have failed */
perror("exec of dbmserv failed");
exit(1);
}
/* better not do anything until the server
* is running. Just wait for the server,
* in this case a child process, gives us
* signal to proceed.
*/
signal(SIGALRM,bad_news);
alarm(30); /* give the server 30 seconds to get it going */
while ((qsema=semget(sema_key,1,0))==ERROR)
{
#ifdef DEBUG
printf("waiting for server\n");
#endif
sleep(1);
}
}
/* get the message queue ids */
if((readq=msgget(makey(RQUEUE),0))==ERROR)
{
qerror("Client getting read queue id");
exit(1);
}
#ifdef DEBUG
else
printf("read queue id is %d\n",readq);
#endif
if((writeq=msgget(makey(WQUEUE),0))==ERROR)
{
qerror("Client getting write queue id");
exit(1);
}
#ifdef DEBUG
else
printf("write queue id is %d\n",readq);
#endif
/*
* Now fork off a bunch of processes, giving each
* one a different starting seed.
* (if fork() returns zero, this is already the child.
*/
i=ntasks;
do {
j=fork();
} while((j!=0)&&(--i!=0));
/*
* Am I a child or a father?
*/
if( j==0)
{
#ifdef DEBUG
printf("I am child client %d\n",getpid());
#endif
/* ***Child process*** */
if((cerr=dotests(i,iters))!=0)
{
fprintf(stderr,"**Child error:\n");
switch(cerr)
{
case(QERR):
qerror(" Error is");
break;
case(SEMERR):
serror(" Error is");
break;
default:
perror(" Error is");
break;
}
exit(1); /* If any error - just die. */
}
else
{
clearrec(myreq.data.recdat);
#ifdef VERBOSE
/*
** Log net elapsed time.
*/
sprintf(myreq.data.recdat,"%#010ld %#010ld %#010ld %#010d ",
tottime.cpu,tottime.system,tottime.real,errcnt);
#else
sprintf(myreq.data.recdat,"%#010d ", errcnt);
#endif
myreq.type=RESULTREQ;
if(semop(qsema,&lockq,1) == ERROR) exit(0);
if(msgsnd(writeq,&myreq,RECLEN,0)<0)
exit(0);
if(semop(qsema,&unlockq,1) == ERROR) exit(0);
exit(0);
}
}
else
{
#ifdef DEBUG
printf("I am parent client %d\n",getpid());
#endif
/* ***Father process*** */
/*
** Wait for the children to complete.
*/
i=ntasks;
while(i--) wait(&waitval);
/*
** Now tell the server to go away.
** Note that if we have trouble (for some reason)
** getting the semaphore...we just barrel on through.
*/
myreq.data.request=DIEREQ;
myreq.type=1L; /* Pid not needed */
if(msgsnd(writeq,&myreq,RECLEN,0)<0)
qerror("**Message queue error during termination\n");
exit(0);
}
}
/********************************* dotests *************************
** Execute tests.
** Input number acts as seed for random number generator.
** Returns -1 if failure.
********************************************************************/
int
dotests(sseed,iter)
int sseed;
int iter; /* Number of iterations */
{
int i,j; /* Loop variables */
int maxid; /* Max ID currently in database */
long mypid; /* Get my process id */
/*
** Initialize the random number seed.
*/
randnum((unsigned long)sseed);
/*
** Initialize error count and timer stuff.
*/
errcnt=0;
/*
** I need to know my process id.
*/
mypid = (long)getpid();
/*
** Find out what the maximum id in the database
** is.
*/
myreq.data.request=GETLREQ;
myreq.type=mypid;
#ifdef DEBUG
printf("About to send 1st transmission\n");
#endif
if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
if(msgsnd(writeq,&myreq,RECLEN,0)<0) return(QERR);
if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0) return(QERR);
if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
#ifdef DEBUG
printf("maxid string = %s\n",hisresp.data.recdat);
#endif
maxid=atoi(hisresp.data.recdat);
#ifdef DEBUG
printf("maxid = %d\n",maxid);
#endif
/*
** Now loop through the tests iter times.
*/
for(i=0;i<iter;i++)
{
#ifdef DEBUG
printf("In outer loop of client\n");
#endif
/* Do 4 reads */
for(j=0;j<4;j++)
{
#ifdef DEBUG
printf("In inner loop of client\n");
#endif
rid=(int)randwc(maxid)+1;
clearrec(myreq.data.recdat);
sprintf(myreq.data.recdat,"%#04d",rid);
myreq.data.request=READREQ;
#ifdef VERBOSE
/* Turn on timer */
stopwatch.real = times(&tbuff);
stopwatch.system = tbuff.tms_stime;
stopwatch.cpu = tbuff.tms_utime;
#endif
#ifdef DEBUG
printf("About to read\n");
#endif
if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
if(msgsnd(writeq,&myreq,RECLEN,0)<0)
return(QERR);
if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0)
return(QERR);
if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
#ifdef VERBOSE
/* Turn off timer */
tottime.real += times(&tbuff)-stopwatch.real;
tottime.system += tbuff.tms_stime-stopwatch.system;
tottime.cpu += tbuff.tms_utime-stopwatch.cpu;
#endif
/* Did we get what we should? */
errcnt+=verify();
}
/* Do 1 write */
rid=(int)randwc(maxid)+1;
clearrec(myreq.data.recdat);
sprintf(myreq.data.recdat,"%#04d",rid);
loadrec((rid-1)%10);
strncpy(myreq.data.recdat+4,myrec.name,RECLEN-4);
myreq.data.request=WRITEREQ;
#ifdef VERBOSE
/* Turn on timer */
stopwatch.real = times(&tbuff);
stopwatch.system = tbuff.tms_stime;
stopwatch.cpu = tbuff.tms_utime;
#endif
if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
if(msgsnd(writeq,&myreq,RECLEN,0)<0)
return(QERR);
if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0)
return(QERR);
if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
#ifdef DEBUG
printf("Message recieved\n");
#endif
#ifdef VERBOSE
/* Turn off timer */
tottime.real += times(&tbuff)-stopwatch.real;
tottime.system += tbuff.tms_stime-stopwatch.system;
tottime.cpu += tbuff.tms_utime-stopwatch.cpu;
#endif
if(hisresp.data.request!=(long)AOK) errcnt++;
/* Sleep a little */
if(sleeptime) sleep(sleeptime);
/* Do an append every 10 times through the loop */
if((i%10)==0)
{
myreq.data.request=GETLREQ;
#ifdef VERBOSE
/* Turn on timer */
stopwatch.real = times(&tbuff);
stopwatch.system = tbuff.tms_stime;
stopwatch.cpu = tbuff.tms_utime;
#endif
if(semop(qsema,&locka,1)==ERROR) return(SEMERR);
if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
if(msgsnd(writeq,&myreq,RECLEN,0)<0) return(QERR);
if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0)
return(QERR);
if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
#ifdef VERBOSE
/* Turn off timer */
tottime.real += times(&tbuff)-stopwatch.real;
tottime.system += tbuff.tms_stime-stopwatch.system;
tottime.cpu += tbuff.tms_utime-stopwatch.cpu;
#endif
maxid=atoi(hisresp.data.recdat);
rid=(maxid+=1);
clearrec(myreq.data.recdat);
sprintf(myreq.data.recdat,"%#04d",rid);
loadrec((rid-1)%10);
strncpy(myreq.data.recdat+4,myrec.name,RECLEN-4);
myreq.data.request=ADDREQ;
#ifdef VERBOSE
/* Turn on timer */
stopwatch.real = times(&tbuff);
stopwatch.system = tbuff.tms_stime;
stopwatch.cpu = tbuff.tms_utime;
#endif
if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
if(msgsnd(writeq,&myreq,RECLEN,0)<0) return(QERR);
if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0)
return(QERR);
if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
if(semop(qsema,&unlocka,1)==ERROR) return(SEMERR);
#ifdef VERBOSE
/* Turn off timer */
tottime.real += times(&tbuff)-stopwatch.real;
tottime.system += tbuff.tms_stime-stopwatch.system;
tottime.cpu += tbuff.tms_utime-stopwatch.cpu;
#endif
if(hisresp.data.request!=(long)AOK) errcnt++;
/* Sleep a little */
if(sleeptime) sleep(sleeptime);
}
}
/*
** All's well that ends well.
*/
return(0);
}
/*
** verify
*/
int verify()
{
char buffr[80];
/* Is there response an error response? */
if(hisresp.data.request!=(long)AOK)
return(1);
/* Was it the number we were looking for? */
strncpy(buffr,hisresp.data.recdat,4);
buffr[4]='\0';
if(atoi(buffr)!=rid)
return(1);
/* Check the record number with its contents */
loadrec((rid-1)%10);
if(strncmp(hisresp.data.recdat+4,myrec.name,RECLEN-4)!=0)
return(1);
/* Looks good */
return(0);
}
/*
** Clear a record
*/
clearrec(rptr)
char *rptr;
{
int i;
for(i=0;i<RECLEN;++i)
*rptr++='\0';
return;
}
/*
** Load the record up with random data.
*/
loadrec(sel)
int sel; /* Select which fake record */
{
char *nname;
char *naddr;
char *nphon;
switch(sel)
{
case 0: nname="Tom Thompson ";
naddr="9401 Billy Willy Road ";
nphon="3334442222";
break;
case 1: nname="Steve Apiki ";
naddr="50 Hawaii Way c/o Jack Lord ";
nphon="1234443333";
break;
case 2: nname="Stan Diehl ";
naddr="27 Hoptoad Hollow Way ";
nphon="3332221111";
break;
case 3: nname="Judy Grehan ";
naddr="Suite 3, WallState Building ";
nphon="9995556666";
break;
case 4: nname="Aaron Richards ";
naddr="Highway 40 OverPass, Second Pylon ";
nphon="8883339999";
break;
case 5: nname="Benjamin Davidson ";
naddr="Under The Bridge, HeavyWater City ";
nphon="7773229988";
break;
case 6: nname="Dayle Woolston ";
naddr="4040 Pleasant Way, WICAT Central ";
nphon="2228332299";
break;
case 7: nname="Jim Carls ";
naddr="Big Oak Tree Behind Barsodie's ";
nphon="32244566657";
break;
case 8: nname="Steve Smith ";
naddr="7000 Aloth Cove ";
nphon="2118332929";
break;
case 9:
default: nname="Blind Willy Chitlins";
naddr="Unavailable Until Further Notice ";
nphon="3456789012";
break;
}
/*
** Fill the structure with fake data.
*/
strncpy(myrec.name,nname,NAMELEN);
strncpy(myrec.address,naddr,ADDRLEN);
strncpy(myrec.phone,nphon,PHONLEN);
return;
}
/*
** randwc(num)
** Returns random modulo num.
*/
long randwc(num)
long num;
{
return(randnum(0L)%num);
}
/*
** randnum(val)
** Second order linear congruential generator.
** Constants suggested by J. G. Skellam.
** If val==0, returns next member of sequence.
** val!=0, "seeds" generator with val.
*/
long randnum(lngval)
unsigned long lngval;
{
register unsigned long interm;
static unsigned long randw[2] = { 13 , 117 };
if (lngval!=0L) randw[1]=lngval;
interm=(randw[0]*254754L+randw[1]*529562L)%999563L;
randw[1]=randw[0];
randw[0]=interm;
return(interm);
}
/************************** makey ******************************
** Following routine converts an ASCII string to a key_t value.
** This routine originally appeared in ADVANCED PROGRAMMERS GUIDE
** TO UNIX SYSTEM V by R. Thomas, PHD; L. R. Rogers, and J. L. Yates.
** Osborne McGraw Hill.
*******************************************************************/
key_t
makey(p)
char *p;
{
key_t keyv;
int i;
if(isnumber(p))
keyv = (key_t)atol(p);
else
{
keyv=(key_t)0;
for(i=0;i<sizeof(key_t) && p[i];i++)
keyv=(keyv << 8) | p[i];
}
return(keyv);
}
/***************************** isnumber *************************/
int isnumber(p)
char *p;
{
for( ; *p && isdigit(*p); p++) ;
return(*p ? 0 : 1);
}
/**************************** badnews **************************/
int bad_news() /* things are screwed up */
{
fprintf(stderr,"TIMED OUT\n");
exit(1);
}
/******************************** qerror **********************
** prints out the errormessage associate with a queue
***************************************************************/
qerror(s)
char *s; /* message prefix string */
{
extern int errno;
fprintf(stderr,"QUEUE ERROR: %s:\n ",s);
switch(errno)
{
case EACCES: fprintf(stderr,
"message queue exists, but locked out (EACCES)\n");
break;
case ENOENT: fprintf(stderr,
"message queue does not exist (ENOENT)\n");
break;
case ENOSPC: fprintf(stderr,
"too manny message queus (ENOSPC)\n");
break;
case EEXIST: fprintf(stderr,
"message queue exists, but locked out (EEXIST)\n");
break;
default: fprintf(stderr,
"unknown error (%n)",errno);
break;
}
return(0);
}
/******************************** serror **********************
** prints out the errormessage associate with a semaphore
***************************************************************/
serror(s)
char *s; /* message prefix string */
{
extern int errno;
fprintf(stderr,"SEMAPHORE ERROR: %s:\n ",s);
switch(errno)
{
case EINVAL: fprintf(stderr,
"invalid number of semaphore sets (EINVAL)\n");
break;
case EACCES: fprintf(stderr,
"semaphore exists, but invalid operation (EACCES)\n");
break;
case ENOENT: fprintf(stderr,
"semaphore does not exist (ENOENT)\n");
break;
case ENOSPC: fprintf(stderr,
"too many semaphores (ENOSPC)\n");
break;
case EEXIST: fprintf(stderr,
"semaphore exists, but locked out (EEXIST)\n");
break;
default: fprintf(stderr,
"unknown error (%n)",errno);
break;
}
return(0);
}