#module CLASS_SCHED "TOY class scheduler X-9" /* **++ ** FACILITY: ** ** VAX/VMS class scheduler ** ** ABSTRACT: ** ** This is a test/demo class scheduler. It places processes into ** classes based upon account string and a few other nits. ** ** AUTHORS: ** ** John C. Hallyburton, Jr. ** ** CREATION DATE: 5-July-1991 ** ** MODIFICATION HISTORY: ** ** X-9 EMB Ellen M. Batbouta 30-Jun-1999 ** Remove load_scheduler routine and the call to it. The ** class scheduler code is now part of the process_management* ** execlets and will always be present. ** ** X-8 JCH602N John C. Hallyburton, Jr. 17-May-1995 ** Fix use of wrong SS$_ code. Remove old edit history entries. ** ** X-7 JCH602M John C. Hallyburton, Jr. 10-Apr-1995 ** Typo in printf in ast_routine. ** **-- */ /* Build instructions: $ CC CLASS/STANDARD=VAXC $ LINK CLASS */ /* ** ** INCLUDE FILES ** */ #include ssdef /* #include cshdef.h */ /*** MODULE CSHDEF IDENT X-1 ***/ /* $SCHED function codes */ #define CSH$_READ_ALL 23 /* Read data on all processes */ #define CSH$_READ_NEW 24 /* Read data on new processes */ #define CSH$_SET_CLASS 25 /* Place process(es) into class(es) */ #define CSH$_RES1 26 #define CSH$_RES2 27 #define CSH$_RES3 28 #define CSH$_RES4 29 #define CSH$_SET_NEW 30 /* Define this process as "new" */ #define CSH$_SET_TIMEOUT 31 /* Set deadman timer */ #define CSH$_RES5 32 #define CSH$_RES6 33 #define CSH$_RES7 34 #define CSH$_RES8 35 #define CSH$_READ_QUANT 36 /* Read per-class quanta remaining */ #define CSH$_SET_QUANT 37 /* Define per-class quanta */ #define CSH$_RES9 38 #define CSH$_RESA 39 #define CSH$_RESB 40 #define CSH$_RESC 41 #define CSH$_SET_ATTN_AST 42 /* Establish attention AST */ #define CSH$_RSED 43 #define CSH$_RESE 44 #define CSH$_RESF 45 #define CSH$_RES0 46 #define CSH$_CLEAR_ATTN_AST 47 /* Clear attention AST */ #define CSH$_MAX_SCHED_FUNC 48 /* AST reason mask */ #define CSH$V_NEW_PROC 1 /* Possible new process */ /* CSHC -- Class ScHeduler Class block */ /* */ /* Used to communicate from user to system, defining process/class mapping */ #define CSHC$K_LENGTH 8 #define CSHC$C_LENGTH 8 typedef struct _CSHC { unsigned long int CSHC$L_EPID; /* EPID of target process */ short int CSHC$W_CLASS; /* Class number, 0-N, %X'FFFF' means */ /* `Take out of class scheduling' */ unsigned short int CSHC$W_WINDFALL; /* Windfall ticks to grant */ } CSHC; /* CSHP -- Class ScHeduler data block for Processes */ /* */ /* Used to communicate from system to user, showing various characteristics */ /* of processes. */ #define CSHP$K_LENGTH 24 #define CSHP$C_LENGTH 24 typedef struct _CSHP { unsigned long int CSHP$L_STATUS; /* Copy of PCB$L_STS */ unsigned short int CSHP$W_PIX; /* Process index slot */ char CSHP$B_PRI; /* Current priority (internal) */ char CSHP$B_PRIB; /* Current base priority (internal) */ unsigned long int CSHP$L_EPID; /* The official PID */ char CSHP$T_ACCOUNT [8]; /* Account string from PCB */ unsigned long int CSHP$L_CPUTIM; /* CPU time charged */ } CSHP; /* End CSHDEF.H */ void ast_routine(); /* "Attention AST" routine */ /* Count of classes and processes this code is built for. More advanced code */ /* will of course have dynamic allocation */ #define CCOUNT (20) #define MAXPROCESSCNT (1000) #define UNDEFINED 5551212 #define NOCLASS 0xFFFF long sts, p2, chunk, i,j,k, ChunkGiven; long *ptr; long classvec[CCOUNT], ccount; CSHC p1[MAXPROCESSCNT]; CSHP procs[MAXPROCESSCNT]; long BytesProcs; /* Count of bytes to send/receive to $SCHED */ /* Stuff used to map accounts to classes; defines class 2 and 3 */ char* account[] = { ""," "," ", "SYSTEM ", "30-PRCNT"}; long class[] = {NOCLASS, NOCLASS, UNDEFINED, 2, 3}; long NumStrings = sizeof(class) / sizeof(class[0]); /* Array used to "stutter" quantum assignments so as to prevent harmonic */ /* buildup, where some jobs get the dregs of the class more than others. */ /* In a real application this is not likely to pose a problem. */ long stutter[] = {-10, -8, -6, -3, -1, 1, 3, 6, 8, 10}; long Nstutter = sizeof(stutter) / sizeof(stutter[0]); /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Maps process(es) to class(es) ** ** FORMAL PARAMETERS: ** ** None ** ** IMPLICIT INPUTS: ** ** procs array filled in with READ_ALL or READ_NEW data ** BytesProcs bytecount of procs array ** ChunkGiven of elements in procs array ** account array of strings ** class array of classes corresponding to entries in account array ** ** IMPLICIT OUTPUTS: ** ** p1 array of process --> class mapping ** $SCHED system service invoked to do the mapping ** ** COMPLETION CODES: ** ** Completion happens only on errors, which are then returned to VMS. ** ** SIDE EFFECTS: ** ** none ** **-- */ void setup_class() { for (i=0,j=0; i < BytesProcs/ChunkGiven; i++) { p1[j].CSHC$L_EPID = procs[i].CSHP$L_EPID; p1[j].CSHC$W_WINDFALL = 0; /* No windfall by default */ p1[j].CSHC$W_CLASS = 0; /* Default if account unrecognized */ for (k=0; k < NumStrings; k++) { /* Special check for "important" system processes: if account is SYSTEM *and* base priority is greater than 4, then do not class-schedule the process. Note: null account strings and " " account strings are already set up to no-class-scheduling. */ if ( (strncmp(account[k], "SYSTEM ",8) == 0) & (procs[j].CSHP$B_PRIB > 4) ) {k=0; p1[j].CSHC$W_CLASS=65535; break;} /* Not "important" process, map account to index */ if ( strncmp(&procs[i].CSHP$T_ACCOUNT, account[k], 8) == 0) { p1[j].CSHC$W_CLASS = class[k]; break; } /* Additional mappings would go here. Remember to "break" after each mapping. */ } /* end for(k=0;...) /* See if account not found. */ if (k == NumStrings) k=0; /* Print out what's going on */ if ( (class[k] != UNDEFINED) & (class[k] != NOCLASS) ) printf("%X in class %d\n", p1[j].CSHC$L_EPID, p1[j].CSHC$W_CLASS); if (class[k] == UNDEFINED) printf("%X no class yet\n", p1[j].CSHC$L_EPID); if (class[k] == NOCLASS) printf("%X not class scheduled\n", p1[j].CSHC$L_EPID); /* Step to next entry supplied */ j++; } chunk = sizeof(p1[0]); p2 = j * chunk; if (p2 > 0) { sts=sys$sched(CSH$_SET_CLASS, &p1, &p2, &chunk); printf("CSH$_SET_CLASS call returns status of %x\n",sts); if (!(sts&1)) sys$exit(sts); } } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Main program. ** ** FORMAL PARAMETERS: ** ** none ** ** IMPLICIT INPUTS: ** ** None ** ** IMPLICIT OUTPUTS: ** ** None ** ** COMPLETION CODES: ** ** Completion happens only on errors, which are then returned to VMS. ** ** SIDE EFFECTS: ** ** Class Scheduling happens ** **-- */ int class_main (int argc, char *argv[]) MAIN_PROGRAM { /* <1> Set process /NOSWAP to assure we don't get swapped out. This is not */ /* absolutely necessary, just a good idea since we execute rather frequently */ sts = sys$setswm(1); if (sts == SS$_NOPRIV) sts = SS$_NOPSWAPM; if (!(sts&1)) sys$exit(sts); /* <2> Attempt a READ_QUANT */ ccount = sizeof(classvec); sts = sys$sched(CSH$_READ_QUANT, &classvec, &ccount, 0); /* <3> Sample use of SET_TIMEOUT function, allow 100 seconds */ sts = sys$sched(CSH$_SET_TIMEOUT, 0, 100, 0); if (!(sts&1)) sys$exit(sts); /* <4> Read about ALL processes */ BytesProcs = sizeof(procs); sts=sys$sched(CSH$_READ_ALL, &procs, &BytesProcs, &ChunkGiven); printf("CSH$_READ_ALL call returns status of %X, chunk size %d\n", sts, ChunkGiven); if (!(sts&1)) sys$exit(sts); /* <5> Define some number of classes so that class assignments */ /* can be checked against the number of classes defined. */ /* Repeating: first define classes (0 to N-1) so that when */ /* we get around to assigning classes to processes, $SCHED */ /* can verify the class actually exists. */ for(i=0; i Assign processes to classes */ setup_class(); /* <7> Define AST routine to execute when something interesting happpens */ sts=sys$sched(CSH$_SET_ATTN_AST, (int *) &ast_routine, (int *) 0, (int *) 0); if (!(sts&1)) sys$exit(sts); for (;;) /* Main scheduling loop */ { long efn = 4, delt[] = { -10 * 10 * 1000 * 1000, -1}; /* Timer control */ /* <8> Give every class a quantum. Work on 10-second (1000-tick) basis */ #define Qstutter (stutter[(rand() % Nstutter)]) /* Map as follows: 0 - 10%, 1 - 25%, 2 - 20%, 3 - 30% */ classvec[0] = 100 + Qstutter; classvec[1] = 250 + Qstutter; classvec[2] = 200 + Qstutter; classvec[3] = 300 + Qstutter; ccount = 16; /* Just classes 0-3 right now * 4 bytes per class */ sts=sys$sched(CSH$_SET_QUANT, &classvec, &ccount, 0); /* printf("CSH$_SET_QUANT call returns status of %X\n",sts); */ if (!(sts&1)) sys$exit(sts); /* Wait a while, then go around again */ sts = sys$setimr(efn, &delt, 0,0,0); if (!(sts&1)) sys$exit(sts); sts = sys$waitfr(efn); } } void ast_routine(int reason) { long asts; printf("\nAST called with reason %x\n",reason); /* Read about NEW processes */ BytesProcs = sizeof(procs); asts=sys$sched(CSH$_READ_NEW, &procs, &BytesProcs, &ChunkGiven); printf("CSH$_READ_NEW call returns status of %X\n", asts); if (!(asts&1)) sys$exit(asts); setup_class(); /* Re-arm attention AST */ asts=sys$sched(CSH$_SET_ATTN_AST, (int *) &ast_routine, (int *) 0, (int *) 0); if (!(asts&1)) sys$exit(asts); return; }