JGilchrist_instructions.txt
To launch: run launch.sh in terminal or run the Commander_process file. The command rocess wil start and fork itself. The fork will replace its contents with Project_Manager project manager is a simulator that simulates this weeks lab.
JGilchrist_lab1 Code.txt
/*
* File: week1ECET360.cpp
* Author: JGilchrist
*
* Created on November 5, 2013, 8:20 PM
*/
#pragma GCC diagnostic ignored “-Wwrite-strings” //compiler directive? Cplusplus.com
#include
#include
#include
#include
#include
using namespace std;
/***************************************
*Used from the the partial code given
****************************************/
/* Commander Process: */
int main(int argc, char** argv) {
int result = 0;
int pfd[2];
pid_t cpid; //cpid of type integer defined types.h //Via Paul Nichols
bool parentLoop = true;//control variable for write loop, break?CPlusplus.con
char userInput; //user input
if (pipe(pfd) == -1) {//creating the pipe
perror(“pipe failed”);
exit(EXIT_FAILURE);
}
//fork a child process after the pipe is made so they both have same PFD via http://www.advancedlinuxprogramming.com/alp-folder/alp-ch03-processes
cpid = fork();
if (cpid == -1) {//if fork returned error exit
perror(“fork failed”); //via Paul Nichols
exit(EXIT_FAILURE);
}
//wait for a second so child doesnt write over parent
sleep(1);//linux sleep 1 second if using windows Sleep(milliseconds)
if(cpid != 0) { //this is the parent–>Commander process*************************************************
close(pfd[0]); //closing read end on parent…parent only writes
cout << "commands are 'Q', 'U', 'P', or 'T' to quit"<
cout << endl; // cout for the user input
if((userInput=='Q') || (userInput=='U')||(userInput=='P')){
/************************************************************************************
* The commander process accepts four commands: Used from the ILab Tab
* 1. Q: End of one unit of time.
* 2. U: Unblock the first simulated process in blocked queue.
* 3. P: Print the current state of the system.
* 4. T: Print the average turn around time, and terminate the system.
*************************************************************************************/
//write info to pipe
if(write(pfd[1], &userInput, sizeof(userInput)) < 0)
exit(EXIT_FAILURE);
//sleep(.5);//linux sleep 1 second if using windows Sleep(milliseconds)
}
else if(userInput == 'T'){
cout << "user input is T...Terminating parent" << endl;
parentLoop = false;
if(write(pfd[1], &userInput, 1) < 0){
perror("pipe write failed");
exit(EXIT_FAILURE);
}
}
else {
cout << "invalid input, please enter values of 'Q', 'U', 'P', or 'T' to quit\n" << endl;
}
}
cout << "waiting for child process to close.."<
//execl( project_manager_loc , “Project_Manager”, argv[1], &string1[0], (char *) 0);
close(pfd[1]);//close write end of pipe
char *project_manager_loc = “./Project_Manager”;
char string1[12]; // A 32 bit number can’t take more than 11 characters, + a terminating 0
snprintf(string1,12,”%i”,pfd[0]); // Copy the file descriptor into a string
char *args_1[] = {“Project_Manager”, argv[0], &string1[0], (char *) 0};
execve( project_manager_loc , args_1, NULL);
cout << "Error executing child...can not open" << endl;
close(pfd[0]);
cout << "exiting" << endl;
}//end process
_exit(result);
}
JGilchrist_Ilab1_execution_file.txt
/*
* File: week1ECET360Execution.cpp
* Author: JGilchrist
* Tutor: Paul Nichols
* References used: http://www.advancedlinuxprogramming.com/alp-folder/alp-ch03-processes
* cplusplus.com, stackedoverflow.com
* Created on November 6, 2013, 10:20 PM
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class Instruction {//file instruction format
public:
char operation;
int intArg;
string stringArg;
};
/*********************************************************************************************************
* Cpu is used to simulate the execution of a simulated process that is in running state.
* It should include data members to store a pointer to the program array, current program counter value,
* integer value, and time slice of that simulated process. In addition, it should store the number of
* time units used so far in the current time slice.
*********************************************************************************************************/
class Cpu {
public:
vector
int programCounter;
int value;
int timeSlice;//curent time slice
int timeSliceUsed;//time units used so far in current time slice
};
enum State {//curent state of pcbEntry
STATE_READY,
STATE_RUNNING,
STATE_BLOCKED
};
class PcbEntry {//class of values to save in PCB table
public:
int processId;
int parentProcessId;
vector
unsigned int programCounter;
int value;
unsigned int priority;
State state;
unsigned int startTime; // For iLab 3, unused for iLab 2
unsigned int timeUsed; // For iLab 3, unused for iLab 2
};
unsigned int timestamp = 0; // The current simulation time.
Cpu cpu; // The current CPU state.
// For the states below, -1 indicates empty (since it is an invalid index).***see State description
/*****************************************************************************************************
* RunningState stores the PcbTable index of the currently running simulated process.
*******************************************************************************************************/
int runningState = -1; // The index of the running process in the PCB table.
/****************************************************************************************************
* ReadyState stores all simulated processes (PcbTable indices) that are ready to run.
* This can be implemented using a queue or priority queue data structure.
****************************************************************************************************/
deque
/*****************************************************************************************************
* BlockedState stores all processes (PcbTable indices) that are currently blocked.
* This can be implemented using a queue data structure.
*******************************************************************************************************/
deque
// In this implementation, we’ll never explicitly clear PCB entries and the
// index in the table will always be the process ID. These choices waste memory,
// but since this program is just a simulation it the easiest approach.
// Additionally, debugging is simpler since table slots and process IDs are
// never re-used.
/*********************************************************************************************************
* PcbTable is an array with one entry for every simulated process that hasn’t finished its execution yet.
* Each entry should include data members to store process id, parent process id, a pointer to program counter
* value (initially 0), integer value, priority, state, start time, and CPU time used so far.
********************************************************************************************************/
vector
double cumulativeTimeDiff = 0;
int numTerminatedProcesses = 0;
// Sadly, C++ has no built-in way to trim strings:
string& trim(string &argument)
{
string whitespace(” \t\n\v\f\r”);
size_t found = argument.find_last_not_of(whitespace);
if (found != string::npos) {
argument.erase(found + 1);
argument.erase(0, argument.find_first_not_of(whitespace));
} else {
argument.clear(); // all whitespace
}
return argument;
}
bool createProgram(const string &filename, vector
{
cout << "running creatProgram process " << endl;
ifstream file; //open a stream to "init" named file
int lineNum = 0;
file.open(filename.c_str());
if (!file.is_open()) {
cout << " file open: Error opening file " << filename << endl;
return false;
}
while (file.good()) {//------------------- process init file --------------------------------------------
string line;
getline(file, line);
cout << " read from " << filename.c_str() << " : " << line << endl;
trim(line);
if (line.size() > 0) {
Instruction instruction;
instruction.operation = toupper(line[0]);
instruction.stringArg = trim(line.erase(0, 1));
stringstream argStream(instruction.stringArg);
switch (instruction.operation) {
case ‘S’: // Integer argument.
case ‘A’: // Integer argument.
case ‘D’: // Integer argument.
case ‘F’: // Integer argument.
if (!(argStream >> instruction.intArg)) {
cout << filename << ":" << lineNum
<< " - Invalid integer argument "
<< instruction.stringArg << " for "
<< instruction.operation << " operation" << endl;
file.close();
return false;
}
break;
case 'B': // No argument.
case 'E': // No argument.
break;
case 'R': // String argument.
// Note that since the string is trimmed on both ends,
// filenames with leading or trailing whitespace (unlikely)
// will not work.
if (instruction.stringArg.size() == 0) {
cout << filename << ":" << lineNum
<< " - Missing string argument" << endl;
file.close();
return false;
}
break;
default:
cout << filename << ":" << lineNum
<< " - Invalid operation, " << instruction.operation
<< endl;
file.close();
return false;
}
program.push_back(instruction);
}
lineNum++;
}//-------------------------------- finished processing the file -------------------------/
file.close();
return true;
}
/*********************************************************************************************
* Instructions S, A and D update the integer value stored in Cpu.
********************************************************************************************/
void set(int value){
cpu.value = value;
cout << "running set process...setting cpu value to "<< cpu.value << endl;
}
void add(int value){
cpu.value = cpu.value + value;
cout << "running +add process new cpu value = "<< cpu.value << endl;
}
void decrement(int value){
cpu.value = cpu.value - value;
cout << "running -decrement process new cpu value = " << cpu.value << endl;
}
// Performs scheduling.
void schedule()
{
cout << "running schedule process ";
cout << " PID = " << runningState << " ";
// 1. Return if there is still a processing running (runningState != -1).
if (runningState == -1 && !readyState.empty()){//nothing running and process is in queue
// 2. Get a new process to run, if possible, from the readystate queue.
runningState = readyState.front();//retrieve the index of the pcbTable that is ready to run
cout << "...loading new process from que pid is " << runningState << endl;
readyState.pop_front();//remove it from queue push back-->pop front;
// 3. If we were able to get a new process to run:
// a. Mark the processing as running (update new process’s PCB state)
//***warning*** if pcbTable[location] does not exist you are exited!!!!
pcbTable[runningState]->state = STATE_RUNNING;
//cout << "SET NEW RUNNING STATE" << endl;
// b. Update the CPU structure with the PCB entry details (program,
// program counter, value, etc.)
cpu.pProgram = &pcbTable[runningState]->program;//the whole list
//cout << "SET NEW PROGRAM" << endl;
cpu.programCounter = pcbTable[runningState]->programCounter;
//cout << "SET NEW PROGRAM COUNTER" << endl;
cpu.value = pcbTable[runningState]->value;
//cout << "SET NEW VALUE" << endl;
cpu.timeSlice = pcbTable[runningState]->startTime;
//cout << "SET NEW TIMESLICE" << endl;
cpu.timeSliceUsed = pcbTable[runningState]->timeUsed;
}
else if ((runningState == -1)&& (readyState.empty())) cout << "nothing in queue and nothing running blocked state = " << blockedState.size()<< " try and Unblock "<< endl;
else cout << "...process is running" << endl;
}
/******************************************************************************************
* Instruction B moves the currently running simulated process to the blocked state
* and moves a process from the ready state to the running state.
* This will result in a context switch.
*****************************************************************************************/
void block()
{
cout << "running block process " << endl;
// 1. Add the PCB index of the running process (stored in runningState) to
// the blocked queue.
blockedState.push_back(runningState);//push back pull front
// 2. Update the process's PCB entry
// a. Change the PCB's state to blocked.
// b. Store the CPU program counter in the PCB's program counter.
// c. Store the CPU's value in the PCB's value
cout << " adding pid = " << pcbTable[runningState]->processId << " to the block queue" <
pcbTable[runningState]->programCounter = cpu.programCounter;
pcbTable[runningState]->value = cpu.value;
// 3. Update the running state to -1 (basically mark no process as running).
// Note that a new process will be chosen to run later (via the Q command
// code calling the schedule() function).
runningState = -1;
}
/******************************************************************************************
* Instruction E terminates the currently running simulated process, frees up all memory
* (e.g. program array) associated with that process and updates the PcbTable.
* A simulated process from the ready state is moved to running state.
* This also results in a context switch.
*******************************************************************************************/
void end()
{
cout << "running end process " << endl;
// TODO:Get the PCB entry of the running process.
cumulativeTimeDiff = timestamp + 1 - pcbTable[runningState]->startTime;
++numTerminatedProcesses;
runningState = -1;
// memory will get freed when the simulation terminates.
}
/*******************************************************************************************************
* Instruction F results in the creation of a new simulated process. A new entry is created in
* the PcbTable for this new simulated process. A new (unique) process id is assigned and the
* parent process id is process id of the parent simulated process. Start time is set to the current
* Time value and CPU time used so far is set to 0. The program array and integer value of the new
* simulated process are a copy of the program array and integer value of the parent simulated process.
* The new simulated process has the same priority as the parent simulated process.
*
* The program counter value of the new simulated process is set to the instruction immediately
* after the F instruction, while the program counter value of the of the parent simulated process
* is set to n instructions after the next instruction (instruction immediately after F).
*
* The new simulated process is created in the ready state.
*******************************************************************************************************/
void fork(int value)
{
cout << "running fork process " << endl;
PcbEntry *myPCB = new PcbEntry();
myPCB->program = pcbTable[runningState]->program;
if ((value > -2) && (value < 1000)){//ensure passed in method not out of bounds
try{//catch all out of range
myPCB->processId = pcbTable.size();//a PCB index+table.size
myPCB->parentProcessId = pcbTable[runningState]->processId;
myPCB->programCounter = cpu.programCounter;
myPCB->value = cpu.value;
myPCB->priority = pcbTable[runningState]->priority;
myPCB->state = STATE_READY;//dont forget to add to ready queue
myPCB->startTime = timestamp;
pcbTable.push_back(myPCB);//queue push back, pull front
readyState.push_back(myPCB->processId);
cpu.programCounter = cpu.programCounter + value;
cout << " forked process, pid = " << myPCB->processId << " and pushed it to the ready queue" << endl;
}
catch (const out_of_range& oor) {
std::cerr << "Out of Range error: " << oor.what() << '\n';
}
catch(const runtime_error& re)
{
cout << "Exception caught: " << re.what() << '\n';
}
}
else cout << "value is out of bounds" << endl;
}
/******************************************************************************************************
* The R instruction results in replacing the process image of the currently
* running simulated process. Its program array is overwritten by the code in file filename,
* program counter value is set to 0, and integer value is undefined. Note that all these changes
* are made only in the Cpu data structure. Process id, parent process id, start time,
* CPU time used so far, state, and priority remain unchanged.
*****************************************************************************************************/
void replace(string &argument)
{
cout << "running replace process opening file " << endl;
cpu.pProgram->clear();//shouldnt I read argument first?
cout << "replace argument is " << argument << endl;
if (!createProgram(argument, *cpu.pProgram)) {//storing file into pProgram
cout << " ERROR can not open file!" << endl;
cpu.programCounter = cpu.programCounter + 1;
//do more error handling
}
else {
cpu.programCounter = 0;
cout << " createprogram..sucess" << endl;
}
}
// Implements the Q command.
/*****************************************************************************************************
* On receiving a Q command,
* the process manager executes the next instruction of the currently running simulated process,
* increments program counter value (except for F or R instructions), increments Time,
* and then performs scheduling. Note that scheduling may involve performing context switching.
*****************************************************************************************************/
void quantum()
{
cout << "starting quantum process " << endl;
Instruction instruction;
if (runningState == -1) {
cout << "No processes are running" << endl;
++timestamp;
return;
}
if (cpu.programCounter < cpu.pProgram->size()) {
instruction = (*cpu.pProgram)[cpu.programCounter];
cpu.programCounter++;
} else {
cout << "End of program reached without E operation" << endl;
instruction.operation = 'E';
}
switch (instruction.operation) {
case 'S':
set(instruction.intArg);
break;
case 'A':
add(instruction.intArg);
break;
case 'D':
decrement(instruction.intArg);
break;
case 'B':
block();
break;
case 'E':
end();
break;
case 'F':
fork(instruction.intArg);
break;
case 'R':
replace(instruction.stringArg);
break;
}
++timestamp;
schedule();
}
// Implements the U command.
void unblock()
{
cout << "entered unblocked method" << endl;
if(!blockedState.empty()){//check for empty queue
int pcbIndex = blockedState.front();
cout << " unblocking PID = " << pcbIndex << " adding it to ready que" << endl;
blockedState.pop_front();
readyState.push_back(pcbIndex);
pcbTable[pcbIndex]->state = STATE_READY;
schedule();
}
else {cout << " blocked buffer was empty" << endl; }
}
/*************************************************************************************************
* On receiving a P command, the process manager spawns a new reporter process.
************************************************************************************************/
void print()
{
cout << "Print command is not implemented until iLab 3" << endl;
}
// Function that implements the process manager.
/*************************************************************************************************
* The process manager process simulates four process management functions:
* 1. creation of new (simulated) processes,
* 2. replacing the current process image of a simulated process with a new process image,
* 3. management of process state transitions,
* 4. process scheduling.
*************************************************************************************************/
int runProcessManager(int fileDescriptor)
{
PcbEntry *pcbEntry = new PcbEntry();
const char *INITPATH = "init";
// Attempt to create the init process.
/*******************************************************************************************************
* The process manager creates[createProgram()] the first simulated process (process id = 0) program from an input file
* (filename: init). This is the only simulated process created by the process manager on its own.
* All other simulated processes are created in response to the execution of the F instruction
* *****************************************************************************************************/
if (!createProgram(INITPATH , pcbEntry->program)) {
delete pcbEntry;
return EXIT_FAILURE;
}
/********************************************************************************************************
* The process manager maintains six data structures: They are in the pcbEntry class
* Time, Cpu, PcbTable, {[state::] ReadyState, BlockedState, and RunningState.}
******************************************************************************************************/
pcbEntry->processId = pcbTable.size();
pcbEntry->parentProcessId = -1;
pcbEntry->programCounter = 0;
pcbEntry->value = 0;
pcbEntry->priority = 0;
pcbEntry->state = STATE_RUNNING;
// Time is an integer variable initialized to zero.
pcbEntry->startTime = 0;
pcbEntry->timeUsed = 0;
/*********************************************************************************************************
* PcbTable is an array with one entry for every simulated process that hasn’t finished its execution yet.
* Each entry should include data members to store process id, parent process id, a pointer to program counter
* value (initially 0), integer value, priority, state, start time, and CPU time used so far.
********************************************************************************************************/
pcbTable.push_back(pcbEntry);
runningState = pcbEntry->processId;
cout << "Running init process, pid = " << pcbEntry->processId << endl;
/*********************************************************************************************************
* Cpu is used to simulate the execution of a simulated process that is in running state.
* It should include data members to store a pointer to the program array, current program counter value,
* integer value, and time slice of that simulated process. In addition, it should store the number of
* time units used so far in the current time slice.
*********************************************************************************************************/
cpu.pProgram = &(pcbEntry->program);
cpu.programCounter = pcbEntry->programCounter;
cpu.value = pcbEntry->value;
timestamp = 0;
double avgTurnaroundTime = 0;
// Loop until a ‘T’ is read, then terminate.
char ch;
do {
// Read a command character from the pipe.
/****************************************************************************************************
* After creating the first process and initializing all its data structures,
* the process manager repeatedly receives and processes one command at a time
* from the commander process (read via the pipe).
* **************************************************************************************************/
if (read(fileDescriptor, &ch, sizeof(ch)) != sizeof(ch)) {
// Assume the parent process exited, breaking the pipe.
break;
}
// Ignore whitespace characters.
if (isspace(ch)) {
continue;
}
// Convert commands to a common case so both lower and uppercase
// commands can be used.
ch = toupper(ch);
switch (ch) {
case ‘Q’:
quantum();
break;
case ‘U’:
unblock();
break;
case ‘P’:
print();
break;
/******************************************************************************************
* On receiving a T command, the process manager first spawns a reporter process and then
* terminates after termination of the reporter process. The process manager ensures that
* no more than one reporter process is running at any moment.
*****************************************************************************************/
case ‘T’:
if (numTerminatedProcesses != 0) {
avgTurnaroundTime = cumulativeTimeDiff
/ (double)numTerminatedProcesses;
}
cout << "The average turnaround time is " << avgTurnaroundTime
<< "." << endl;
break;
default:
cout << "Unknown command, " << ch << endl;
}
} while (ch != 'T');
// Cleanup any remaining PCB entries.
for (vector
it != pcbTable.end(); it++) {
delete *it;
}
pcbTable.clear();
return EXIT_SUCCESS;
}
//recieves a pipe descriptor
int main(int argc, char** argv) {
//variables}
int result;
int pfd[2];
//read arguments
if(argc < 3) {
cout << "You must provide a pipe adress descriptor " << endl;
// Keeep command window open until user presses ENTER key
cout << "Press ENTER key to exit" << endl;
cin.ignore( );
exit(0);
}
pfd[0] = atoi(argv[2]);
// Run the process manager.
result = runProcessManager(pfd[0]);
// Close the read end of the pipe for the process manager process (for
// cleanup purposes).
cout << "closing pipe...GOODBYE" << endl;
close(pfd[0]);//close read end of pipe
//exit (using the _exit() system call).
exit(result);
// Keeep command window open until user presses ENTER key
cout << "Press ENTER key to exit" << endl;
cin.ignore( );
return(0);
}
Archive created by free jZip.url
[InternetShortcut]
URL=http://www.jzip.com/archive_link
JGilchrist_Lab3_7.txt
#define ENABLE_COMMANDER
#define ENABLE_REPORTER
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class Instruction {
public:
char operation;
int intArg;
string stringArg;
};
class Cpu {
public:
vector
int programCounter;
int value;
int timeSlice;
int timeSliceUsed;
};
enum State {
STATE_READY,
STATE_RUNNING,
STATE_BLOCKED,
STATE_END
};
class PcbEntry {
public:
int processId;
int parentProcessId;
vector
unsigned int programCounter;
int value;
unsigned int priority;
State state;
unsigned int startTime;
unsigned int timeUsed;
};
// The number of valid priorities.
#define NUM_PRIORITIES 4
// An array that maps priorities to their allotted time slices.
static const unsigned int PRIORITY_TIME_SLICES[NUM_PRIORITIES] = {
1,
2,
4,
8
};
unsigned int timestamp = 0;
Cpu cpu;
// For the states below, -1 indicates empty (since it is an invalid index).
int runningState = -1; // The index of the running process in the PCB table.
// readyStates is an array of queues. Each queue holds PCB indices for ready processes
// of a particular priority.
deque
deque
deque
// In this implementation, we’ll never explicitly clear PCB entries and the
// index in the table will always be the process ID. These choices waste memory,
// but since this program is just a simulation it the easiest approach.
// Additionally, debugging is simpler since table slots and process IDs are
// never re-used.
vector
double cumulativeTimeDiff = 0;
int numTerminatedProcesses = 0;
// Sadly, C++ has no built-in way to trim strings:
string &trim(string &argument)
{
string whitespace(” \t\n\v\f\r”);
size_t found = argument.find_last_not_of(whitespace);
if (found != string::npos) {
argument.erase(found + 1);
argument.erase(0, argument.find_first_not_of(whitespace));
} else {
argument.clear(); // all whitespace
}
return argument;
}
bool createProgram(const string &filename, vector
{
ifstream file;
int lineNum = 0;
program.clear();
file.open(filename.c_str());
if (!file.is_open()) {
cout << "Error opening file " << filename << endl;
return false;
}
while (file.good()) {
string line;
getline(file, line);
trim(line);
if (line.size() > 0) {
Instruction instruction;
instruction.operation = toupper(line[0]);
instruction.stringArg = trim(line.erase(0, 1));
stringstream argStream(instruction.stringArg);
switch (instruction.operation) {
case ‘S’: // Integer argument.
case ‘A’: // Integer argument.
case ‘D’: // Integer argument.
case ‘F’: // Integer argument.
if (!(argStream >> instruction.intArg)) {
cout << filename << ":" << lineNum
<< " - Invalid integer argument "
<< instruction.stringArg << " for "
<< instruction.operation << " operation" << endl;
file.close();
return false;
}
break;
case 'B': // No argument.
case 'E': // No argument.
break;
case 'R': // String argument.
// Note that since the string is trimmed on both ends,
// filenames with leading or trailing whitespace (unlikely)
// will not work.
if (instruction.stringArg.size() == 0) {
cout << filename << ":" << lineNum
<< " - Missing string argument" << endl;
file.close();
return false;
}
break;
default:
cout << filename << ":" << lineNum
<< " - Invalid operation, " << instruction.operation
<< endl;
file.close();
return false;
}
program.push_back(instruction);
}
lineNum++;
}
file.close();
return true;
}
// Implements the S operation.
void set(int value)
{
cpu.value = value;
cout << "Set CPU value to " << value << endl;
}
// Implements the A operation.
void add(int value)
{
cpu.value += value;
cout << "Incremented CPU value by " << value << endl;
}
// Implements the D operation.
void decrement(int value)
{
cpu.value -= value;
cout << "Decremented CPU value by " << value << endl;
}
// Performs scheduling.
void schedule()
{
// TODO: Debug and test
//If runningState != -1 AND the cpu.timeSliceUsed equals or exceeds
// cpu.timeSlice:
cout <<" cpu timeslice Used " << cpu.timeSliceUsed<
// 1. Get the PCB entry for runningState.
PcbEntry *myPCB = pcbTable[runningState];
// 2. Lower the process priority (remember since the highest priority is zero,
// you actually have to increment the priority to lower it). Also make sure to
// not increment the priority past its maximum value.
if (myPCB->priority >= 0 && myPCB->priority < (NUM_PRIORITIES - 1)) myPCB->priority++;
// 3. Push runningState on to the end of the correct readyStates queue (hint: use
// pcbEntry.priority to select the correct queue.
switch (myPCB->priority)
{
case 0 :
readyStates[0].push_back(runningState);
break;
case 1 :
readyStates[1].push_back(runningState);
break;
case 2 :
readyStates[2].push_back(runningState);
break;
case 3 :
readyStates[3].push_back(runningState);
break;
default:
cout << "Invalid running state" << endl;
break;
}
// 4. Update the pcbEntry:
// a. Set state to STATE_READY.
// b. Set the programCounter to cpu.programCounter.
// c. Set the value to cpu.value.
// d. Increment timeUsed by cpu.timeSliceUsed.
myPCB->state = STATE_READY;
myPCB->programCounter = cpu.programCounter;
myPCB->value = cpu.value;
myPCB->timeUsed = myPCB->timeUsed + cpu.timeSliceUsed;
cout << " time this process has used = " << myPCB->timeUsed << endl;
cout << " scheduler is ending process and decreasing priority to "<< myPCB->priority <<"\n" << endl;
// 5. Set runningState to -1.
runningState = -1;
}
if (runningState != -1) {//the correct priority program is running
cout << " correct process is running exiting scheduler" << endl;
return;
}
cout << " loading new process from ";
// TODO: Debug and test
// Get a new process to run, if possible, from the ready queue in
// priority order. Remember that the highest priority is zero! The code below
// needs to be updated/replaced since right now it only uses the highest priority
// queue.
if(!readyStates[0].empty()){
runningState = readyStates[0].front();
cout << "priority 0 " << endl;
readyStates[0].pop_front();
}
else if(!readyStates[1].empty()){
runningState = readyStates[1].front();
cout << "priority 1" << endl;
readyStates[1].pop_front();
}
else if(!readyStates[2].empty()){
runningState = readyStates[2].front();
cout << "priority 2 " << endl;
readyStates[2].pop_front();
}
else if(!readyStates[3].empty()){
runningState = readyStates[3].front();
cout << "priority 3 " << endl;
readyStates[3].pop_front();
}
else cout << "ERROR ready state has invalid state entries";
// Make sure there is a process to run.
if (runningState != -1) {
// Mark the process as running.
PcbEntry *pcbEntry = pcbTable[runningState];
pcbEntry->state = STATE_RUNNING;
// Update the CPU with new PCB entry details.
cpu.pProgram = &pcbEntry->program;
cpu.programCounter = pcbEntry->programCounter;
cpu.value = pcbEntry->value;
// TODO: Debug and test
// Set cpu.timeSlice to the correct value (hint: use the
// global PRIORITY_TIME_SLICES array and pcbEntry->priority)
switch (pcbEntry->priority)
{
case 0 :
cpu.timeSlice = PRIORITY_TIME_SLICES[0];
cout << " setting cpu.timeslice to 1"<
cpu.timeSliceUsed = 0;
cout << "Process running, pid = " << pcbEntry->processId << endl; } } // Implements the B operation. void block() { PcbEntry *pcbEntry = pcbTable[runningState]; //TODO there is a problem with pcb time used counter will this fix it pcbEntry->timeUsed += cpu.timeSliceUsed + 1;//add 1 for CPU time to block the process
// TODO: Debug and test
// Raise the process priority (remember since the highest priority is zero,
// you actually have to decrement the priority to raise it). Also make sure to
// not decrement the priority below zero.
if (pcbEntry->priority > 0) pcbEntry->priority–;
blockedState.push_back(runningState);
pcbEntry->state = STATE_BLOCKED;
pcbEntry->programCounter = cpu.programCounter;
pcbEntry->value = cpu.value;
runningState = -1;
cout << "Blocked process, pid = " << pcbEntry->processId << endl; } // Implements the E operation. void end() { PcbEntry *pcbEntry = pcbTable[runningState]; //TODO there is a problem with pcb timused will this fix it? pcbEntry->timeUsed = pcbEntry->timeUsed + cpu.timeSliceUsed + 1;//add 1 to account for e operation
// Add 1 to account for the time to execute the E operation.
cumulativeTimeDiff += (double)(timestamp + 1 – pcbEntry->startTime);
numTerminatedProcesses++;
cout << "Ended process, pid = " << pcbEntry->processId << endl;
pcbEntry->state = STATE_END;
deadState.push_back(runningState);
runningState = -1;
}
// Implements the F operation.
void fork(int value)
{
int pcbIndex = (int)pcbTable.size();
PcbEntry *runningPcbEntry = pcbTable[runningState];
PcbEntry *pcbEntry = new PcbEntry();
pcbEntry->processId = pcbIndex;
pcbEntry->parentProcessId = runningPcbEntry->processId;
pcbEntry->program = runningPcbEntry->program;
pcbEntry->programCounter = cpu.programCounter;
pcbEntry->value = cpu.value;
pcbEntry->priority = runningPcbEntry->priority;
pcbEntry->state = STATE_READY;
pcbEntry->startTime = timestamp + 1;
pcbEntry->timeUsed = 0;
pcbTable.push_back(pcbEntry);
// TODO: debug and test
//Update the line below to use the correct readyStates queue.
readyStates[pcbEntry->priority].push_back(pcbIndex);
cout << "Forked new process, pid = " << pcbEntry->processId << endl;
if ((value < 0) ||
(cpu.programCounter + value >= (int)cpu.pProgram->size())) {
cout << "Error executing F operation, ending parent process" << endl;
end();
}
cpu.programCounter += value;
}
// Implements the R operation.
void replace(string &argument)
{
if (!createProgram(argument, *cpu.pProgram)) {
cout << "Error executing R operation, ending process" << endl;
end();
return;
}
cpu.programCounter = 0;
cout << "Replaced process with " << argument << ", pid = "
<< pcbTable[runningState]->processId << endl;
}
// Implements the Q command.
void quantum()
{
Instruction instruction;
if (runningState == -1) {
cout << "No processes are running" << endl;
++timestamp;
return;
}
if (cpu.programCounter < (int)cpu.pProgram->size()) {
instruction = (*cpu.pProgram)[cpu.programCounter];
cpu.programCounter++;
} else {
cout << "End of program reached without E operation" << endl;
instruction.operation = 'E';
}
switch (instruction.operation) {
case 'S':
set(instruction.intArg);
break;
case 'A':
add(instruction.intArg);
break;
case 'D':
decrement(instruction.intArg);
break;
case 'B':
block();
break;
case 'E':
end();
break;
case 'F':
fork(instruction.intArg);
break;
case 'R':
replace(instruction.stringArg);
break;
}
timestamp++;
// TODO: degug and test
// Increment cpu.timeSliceUsed.
cpu.timeSliceUsed++;
schedule();
}
// Implements the U command.
void unblock()
{
if (!blockedState.empty()) {
int pcbIndex = blockedState.front();
PcbEntry *pcbEntry = pcbTable[pcbIndex];
blockedState.pop_front();
// TODO: debug and test. PHIL << "this process does not increment the timeStamp, because it comes from Commander?"
// Update the line below to use the correct readyStates queue.
readyStates[pcbEntry->priority].push_back(pcbIndex);
pcbEntry->state = STATE_READY;
cout << "Unblocked process, pid = " << pcbEntry->processId << endl;
}
schedule();
}
void printReadyQue(int *myArray){
for (unsigned int j = 0; j < NUM_PRIORITIES; j++){
if (!readyStates[j].empty()){
cout << "READY STATE PRIORITY " << j << " QUEUE:\n pid | ppid | priority | value | start time | cpu time used |" << endl;
for (unsigned int i = 0; i < readyStates[j].size(); i++) {
int pcbIndex = readyStates[j][i];
PcbEntry *pcbEntry = pcbTable[pcbIndex];
printf("% *d % *d % *d % *d % *d % *d \n" , 7 , pcbEntry->processId , 11, pcbEntry->parentProcessId, 10, pcbEntry->priority,
10, pcbEntry->value, 10, pcbEntry->startTime, 10, pcbEntry->timeUsed);
myArray[j] = myArray[j]+1;//increment prioriety
myArray[4] += pcbEntry->timeUsed;//sum
}
}
}
}
void printProcess(int *myArray){
int myTimeUsed = 0;
const int NUM_STATE = 4;
State myState[NUM_STATE] = {STATE_READY , STATE_RUNNING, STATE_BLOCKED, STATE_END};
for (int j =1; j < NUM_STATE; j++){
switch (j) {
case 1:
cout << "RUNNING PROCESSSES:\n pid | ppid | priority | value | start time | cpu time used |" << endl;
break;
case 2:
if (!blockedState.empty())
cout << "BLOCKED PROCESSSES:\n pid | ppid | priority | value | start time | cpu time used |" << endl;
break;
case 3:
if (!deadState.empty())
cout << "DEAD PROCESSSES:\n pid | ppid | priority | value | start time | cpu time used |" << endl;
break;
default:
cout << "ERROR!!!!!";
break;
}
for(unsigned int readyEntry = 0; readyEntry < pcbTable.size(); readyEntry++){
if (pcbTable[readyEntry]->state == myState[j] ){
if (pcbTable[readyEntry]->state == STATE_RUNNING)myTimeUsed = pcbTable[runningState]->timeUsed+cpu.timeSliceUsed;
else myTimeUsed = pcbTable[readyEntry]->timeUsed;
printf(“% *d % *d % *d % *d % *d % *d \n” , 7 , pcbTable[readyEntry]->processId , 11, pcbTable[readyEntry]->parentProcessId, 10, pcbTable[readyEntry]->priority,
10, pcbTable[readyEntry]->value, 10, pcbTable[readyEntry]->startTime, 10, myTimeUsed);
myArray[pcbTable[readyEntry]->priority]++;//increment prioriety
myArray[4] += myTimeUsed;
}
}
}
}
void printPriorieties(int *prioritieList){
cout << endl;
for (int i =0; i<4; i++){
if ( prioritieList[i] != 0)
cout << "number of processes with priority "<< i << " is: " << prioritieList[i] << endl;
}
if ((int)timestamp == prioritieList[4]) cout << "\nCURRENT TIME: " << timestamp << " = " << "total CPU TIME USED: "<< prioritieList[4] << endl;
else cout << "PCB total time does not match this can only happen if no process was running" << endl;
}
// Implements the P command.
void print()
{
#ifdef ENABLE_REPORTER
pid_t pid;
pid = fork();
if (pid == -1) {
cout << "fork:" << strerror(errno) << endl;
return;
}
if (pid != 0) {
// Wait for the reporter process to exit.
wait(NULL);
return;
}
#endif
#ifdef ENABLE_REPORTER
// TODO: debug and test
//Implement all of the printing logic.
int prioritieList[NUM_PRIORITIES + 1] = {0,0,0,0,0};//+ one for sum[4] counter
cout << "\n----------------------------------------------------------------------" << endl;
//cout << "CURRENT TIME: " << timestamp<< endl;
printReadyQue(prioritieList);//prints the processes in ready state by que
printProcess(prioritieList);// prints the rest of the processes
printPriorieties(prioritieList);
cout << "----------------------------------------------------------------------\n"<
delete pcbEntry;
return EXIT_FAILURE;
}
pcbEntry->processId = (int)pcbTable.size();
pcbEntry->parentProcessId = -1;
pcbEntry->programCounter = 0;
pcbEntry->value = 0;
pcbEntry->priority = 0;
pcbEntry->state = STATE_RUNNING;
pcbEntry->startTime = 0;
pcbEntry->timeUsed = 0;
pcbTable.push_back(pcbEntry);
runningState = pcbEntry->processId;
cout << "Running init process, pid = " << pcbEntry->processId << endl;
cpu.pProgram = &(pcbEntry->program);
cpu.programCounter = pcbEntry->programCounter;
cpu.value = pcbEntry->value;
timestamp = 0;
double avgTurnaroundTime = 0;
// Loop until a ‘T’ is read, then terminate.
char ch;
do {
// Read a command character from the pipe.
if (read(fileDescriptor, &ch, sizeof(ch)) != sizeof(ch)) {
// Assume the parent process exited, breaking the pipe.
break;
}
// Ignore whitespace characters.
if (isspace(ch)) {
continue;
}
// Convert commands to a common case so both lower and uppercase
// commands can be used.
ch = toupper(ch);
switch (ch) {
case ‘Q’:
quantum();
break;
case ‘U’:
unblock();
break;
case ‘P’:
print();
break;
case ‘T’:
if (numTerminatedProcesses != 0) {
avgTurnaroundTime = cumulativeTimeDiff
/ (double)numTerminatedProcesses;
}
cout << "The average turnaround time is " << avgTurnaroundTime
<< "." << endl;
break;
default:
cout << "Unknown command, " << ch << endl;
break;
}
} while (ch != 'T');
// Cleanup any remaining PCB entries.
for (vector
it != pcbTable.end(); it++) {
delete *it;
}
pcbTable.clear();
return EXIT_SUCCESS;
}
// Main function that implements the commander.
int main(int argc, char *argv[])
{
#ifdef ENABLE_COMMANDER
int pipeDescriptors[2];
pid_t processMgrPid;
char ch;
int result;
// Create a pipe.
if (pipe(pipeDescriptors) == -1) {
// Print an error message to help debugging.
cout << "pipe: " << strerror(errno) << endl;
return EXIT_FAILURE;
}
// Create the process manager process.
processMgrPid = fork();
if (processMgrPid == -1) {
// Print an error message to help debugging.
cout << "fork: " << strerror(errno) << endl;
return EXIT_FAILURE;
}
if (processMgrPid == 0) {
// The process manager process is running.
// Close the unused write end of the pipe for the process manager
// process.
close(pipeDescriptors[1]);
// Run the process manager.
result = runProcessManager(pipeDescriptors[0]);
// Close the read end of the pipe for the process manager process (for
// cleanup purposes).
close(pipeDescriptors[0]);
_exit(result);
} else {
// The commander process is running.
// Close the unused read end of the pipe for the commander process.
close(pipeDescriptors[0]);
// Loop until a 'T' is written or until the pipe is broken.
do {
sleep(1);
cout << "Input a command: ";
// Read a command character from the standard input.
cin >> ch;
cout << endl;
// Pass commands to the process manager process via the pipe.
if (write(pipeDescriptors[1], &ch, sizeof(ch)) != sizeof(ch)) {
// Assume the child process exited, breaking the pipe.
break;
}
} while (ch != 'T');
// Wait for the process manager to exit.
wait(&result);
// Close the write end of the pipe for the commander process (for
// cleanup purposes).
close(pipeDescriptors[1]);
}
return result;
#else
// Run the Process Manager directly.
return runProcessManager(fileno(stdin));
#endif
}
Week 3 x
Week 3: Process Management (Cont.): Scheduling and Deadlocks - iLab
Print This Page
Connect to the iLab here
.
Lab 3 of 7: Process Management Simulation (50 Points)
Submit your assignment to the Dropbox located on the silver tab at the top of this page.
See Syllabus/"Due Dates for Assignments & Exams" for due date information.
L A B A C C E S S
Accessing Your Lab
To access your labs after you've logged in, apply the following steps:
· Click the Professional Development tab located on the top of the page.
· Select "Development Paths."
You will see the course that you are taking listed on this page.
· Click the course to see the labs.
Starting Your Lab
There are two ways to start a lab activity:
· Schedule it. (Recommended)
Because you have access to live equipment, there is only a defined number of students who can use any one lab at any given time. It is recommended to schedule your labs to make sure the lab is available to you at your convenience. If you try to log into a lab and the access is denied, the system will let you schedule the lab at a later date.
· Start it right away.
L A B O V E R V I E W
Scenario/Summary
Process Management Simulation (Part 3 of 3)
The objective of this three section lab is to simulate four process management functions: process creation, replacing the current process image with a new process image, process state transition, and process scheduling.
This lab will be due over the first three weeks of this course. The commander process program is due in Week 1. This program will introduce the student to system calls and other basic operating system functions. The process manager functions “ process creation, replacing the current process image with a new process image and process state transition “ are due in Week 2. The scheduling section of the process manager is due in Week 3.
You will use Linux system calls such as fork( ), exec(), wait( ), pipe( ), and sleep( ). Read man pages of these system calls for details.
This simulation exercise consists of three processes running on a Linux environment: commander, process manager, and reporter. There is one commander process (this is the process that starts your simulation), one process manager process that is created by the commander process, and a number of reporter processes that get created by the process manager, as needed.
1. Commander Process:
The commander process first creates a pipe and then the process manager process. It then repeatedly reads commands from the standard input and passes them to the process manager process via the pipe. The commander process accepts four commands:
1. Q: End of one unit of time.
2. U: Unblock the first simulated process in blocked queue.
3. P: Print the current state of the system.
4. T: Print the average turnaround time, and terminate the system.
Command T can only be executed once.
1.1 Simulated Process:
Process management simulation manages the execution of simulated processes. Each simulated process is comprised of a program that manipulates the value of a single integer variable. Thus the state of a simulated process at any instant is comprised of the value of its integer variable and the value of its program counter.
A simulated process™ program consists of a sequence of instructions. There are seven types of instructions as follows:
1. S n: Set the value of the integer variable to n, where n is an integer.
2. A n: Add n to the value of the integer variable, where n is an integer.
3. D n: Subtract n from the value of the integer variable, where n is an integer.
4. B: Block this simulated process.
5. E: Terminate this simulated process.
6. F n: Create a new (simulated) process. The new (simulated) process is an exact copy of the parent (simulated) process. The new (simulated) process executes from the instruction immediately after this (F) instruction, while the parent (simulated) process continues its execution n instructions after the next instruction.
7. R filename: Replace the program of the simulated process with the program in the file filename, and set program counter to the first instruction of this new program.
An example of a program for a simulated process is as follows:
S 1000
A 19
A 20
D 53
A 55
F 1
R file_a
F 1
R file_b
F 1
R file_c
F 1
R file_d
F 1
R file_e
E
You may store the program of a simulated process in an array, with one array entry for each instruction.
2. Process Manager Process:
The process manager process simulates four process management functions: creation of new (simulated) processes, replacing the current process image of a simulated process with a new process image, management of process state transitions, and process scheduling. In addition, it spawns a reporter process whenever it needs to print out the state of the system.
The process manager creates the first simulated process (process ID = 0) program from an input file (filename: init). This is the only simulated process created by the process manager on its own. All other simulated processes are created in response to the execution of the F instruction (read from the simulated processes).
2.1 Data structures:
The process manager maintains six data structures: Time, Cpu, PcbTable, ReadyState, BlockedState, and RunningState.
1. Time is an integer variable initialized to zero.
2. Cpu is used to simulate the execution of a simulated process that is in running state. It should include data members to store a pointer to the program array, current program counter value, integer value, and time slice of that simulated process. In addition, it should store the number of time units used so far in the current time slice.
3. PcbTable is an array with one entry for every simulated process that hasn't finished its execution yet. Each entry should include data members to store process ID, parent process ID, a pointer to program counter value (initially 0), integer value, priority, state, start time, and CPU time used so far.
4. ReadyState stores all simulated processes (PcbTable indices) that are ready to run. This can be implemented using a queue or priority queue data structure.
5. BlockedState stores all processes (PcbTable indices) that are currently blocked. This can be implemented using a queue data structure.
6. RunningState stores the PcbTable index of the currently running simulated process.
2.2 Processing input commands:
After creating the first process and initializing all its data structures, the process manager repeatedly receives and processes one command at a time from the commander process (read via the pipe). On receiving a Q command, the process manager executes the next instruction of the currently running simulated process, increments program counter value (except for F or R instructions), increments Time, and then performs scheduling. Note that scheduling may involve performing context switching.
On receiving a U command, the process manager moves the first simulated process in the blocked queue to the ready state queue array. On receiving a P command, the process manager spawns a new reporter process. On receiving a T command, the process manager first spawns a reporter process and then terminates after termination of the reporter process. The process manager ensures that no more than one reporter process is running at any moment.
2.3 Executing simulated processes:
The process manager executes the next instruction of the currently running simulated process on receiving a Q command from the commander process. Note that this execution is completely confined to the Cpu data structure, i.e., PcbTable is not accessed.
Instructions S, A, and D update the integer value stored in Cpu. Instruction B moves the currently running simulated process to the blocked state and moves a process from the ready state to the running state. This will result in a context switch. Instruction E terminates the currently running simulated process, frees up all memory (e.g., program array) associated with that process and updates the PcbTable. A simulated process from the ready state is moved to running state. This also results in a context switch.
Instruction F results in the creation of a new simulated process. A new entry is created in the PcbTable for this new simulated process. A new (unique) process ID is assigned and the parent process ID is the process ID of the parent simulated process. Start time is set to the current Time value and CPU time used so far is set to 0. The program array and integer value of the new simulated process are a copy of the program array and integer value of the parent simulated process. The new simulated process has the same priority as the parent simulated process. The program counter value of the new simulated process is set to the instruction immediately after the F instruction, while the program counter value of the of the parent simulated process is set to
n
instructions after the next instruction (instruction immediately after F). The new simulated process is created in the ready state.
Finally, the R instruction results in replacing the process image of the currently running simulated process. Its program array is overwritten by the code in file filename, program counter value is set to 0, and integer value is undefined. Note that all these changes are made only in the Cpu data structure. Process ID, parent process ID, start time, CPU time used so far, state, and priority remain unchanged.
2.4 Scheduling
The process manager also implements a scheduling policy. You may experiment with a scheduling policy of multiple queues with priority classes. In this policy, the first simulated process (created by the process manager) starts with priority 0 (highest priority). There are a maximum of four priority classes. Time slice (quantum size) for priority class 0 is 1 unit of time; time slice for priority class 1 is 2 units of time; time slice for priority class 2 is 4 units of time; and time slice for priority class 3 is 8 units of time. If a running process uses its time slice completely, it is preempted and its priority is lowered. If a running process blocks before its allocated quantum expires, its priority is raised.
3. Reporter Process
The reporter process prints the current state of the system on the standard output and then terminates. The output from the reporter process appears as follows:
****************************************************************
The current system state is as follows:
****************************************************************\\
CURRENT TIME: time
RUNNING PROCESS:
pid, ppid, priority, value, start time, CPU time used so far
BLOCKED PROCESSES:
Queue of blocked processes:
pid, ppid, priority, value, start time, CPU time used so far
¦
pid, ppid, priority, value, start time, CPU time used so far
PROCESSES READY TO EXECUTE:
Queue of processes with priority 0:
pid, ppid, value, start time, CPU time used so far
pid, ppid, value, start time, CPU time used so far
¦
¦
Queue of processes with priority 3:
pid, ppid, value, start time, CPU time used so far
pid, ppid, value, start time, CPU time used so far
****************************************************************
Deliverables
You will submit three separate files to the dropbox for Week 3:
1. C or C++ program (source code)
2. Executable file (object), and
3. Instructions to execute the program
L A B S T E P S
Process Manager Scheduling (25 points)
The programs for the process scheduling and the reporter process are due this week. These programs are to be written in C or C++ programming languages on a Linux environment.
IMPORTANT: Please make sure that any questions or clarification about these labs are addressed early.
2.4 Scheduling
The process manager also implements a scheduling policy. You may experiment with a scheduling policy of multiple queues with priority classes. In this policy, the first simulated process (created by the process manager) starts with priority 0 (highest priority). There are a maximum of four priority classes. Time slice (quantum size) for priority class 0 is 1 unit of time; time slice for priority class 1 is 2 units of time; time slice for priority class 2 is 4 units of time; and time slice for priority class 3 is 8 units of time. If a running process uses its time slice completely, it is preempted and its priority is lowered. If a running process blocks before its allocated quantum expires, its priority is raised.
Reporter Process Requirements (25 points)
3. Reporter Process
The reporter process prints the current state of the system on the standard output and then terminates. The output from the reporter process appears as follows:
****************************************************************
The current system state is as follows:
****************************************************************\\
CURRENT TIME: time
RUNNING PROCESS:
pid, ppid, priority, value, start time, CPU time used so far
BLOCKED PROCESSES:
Queue of blocked processes:
pid, ppid, priority, value, start time, CPU time used so far
¦
pid, ppid, priority, value, start time, CPU time used so far
PROCESSES READY TO EXECUTE:
Queue of processes with priority 0:
pid, ppid, value, start time, CPU time used so far
pid, ppid, value, start time, CPU time used so far
¦
¦
Queue of processes with priority 3:
pid, ppid, value, start time, CPU time used so far
pid, ppid, value, start time, CPU time used so far
****************************************************************