Example #1
0
/**
	Service event to wait on an I/O operation.

	This function services requests to check an I/O operation for completion.
	If the operation is complete, the process resumes execution immediately;
	otherwise, the process is blocked.

	<b> Important Notes:</b>
	\li Events of type WIO_EVT will result in this call via
	Interrupt_Handler() in simulator.c after interrupt() in obj1.c sets the
	global Event variable.
	\li WIO, unlike SIO, is a blocking I/O operation. As such, the program
	causing the event will be blocked (removed from the ready queue or from the
	CPU) after this event is serviced. However, if I/O had already been
	started from an earlier SIO event, then the process may be able to
	continue, but only if the I/O request has already been completed.
	\li Retrieving the request instruction (REQ) from memory is done similarly
	to finding the device ID in Alloc_rb(). The saved CPU information is used
	to find the necessary segment in the PCB's segment table. Once the segment
	has been found, the segment base is added to the current PC offset (minus
	1) to get the position in memory that the request instruction is located
	at.
	\li The request instruction's operand, which is an address, should be
	passed to Find_rb() to find the needed request block as the request address
	holds information pointing to the device being waited on.

	<b> Algorithm: </b>
	\verbatim
	Retrieve pcb from the terminal table that is waiting for I/O

	Retrieve request instruction using saved CPU information

	Locate request block that process wants to wait for--call Find_rb()

	Determine status of request block
		If rb is still active or pending
			Block process since I/O not finished:
				Mark PCB as blocked; mark it

				Turn CPU and scheduling switches on to schedule a new process

				Calculate active time for process and busy time for CPU

				Record time process was blocked

				Print output message giving blocked procese and burst count

		If rb has finished
			Delete it and keep running current process:
				Delete rb from pcb's list

				Turn CPU switch on to execute a process
				Turn scheduling flag off to use current process (i.e., not schedule a new process)

	\endverbatim
 */
void
Wio_Service( )
{
    //Retrieve pcb from the terminal table that is waiting for I/O
    //~ pcb_type *pcb = Term_Table[Agent-1];
    pcb_type *pcb = Term_Table[Agent - 1]->rb_q->rb->pcb;

    //Retrieve request instruction using saved CPU information
    //~ instr_type instr = Mem[pcb->seg_table[pcb->cpu_save.pc.segment].base + pcb->cpu_save.pc.offset-1];
    instr_type instr = Mem[pcb->seg_table[pcb->cpu_save.pc.segment].base + CPU.state.pc.offset-1];

    //Locate request block that process wants to wait for--call Find_rb()
    rb_type *rb = Find_rb(pcb, &instr.operand.address);

    //Determine status of request block
    //If rb is still active or pending
    if(rb->status == ACTIVE_RB || rb->status == PENDING_RB)
    {
        //Block process since I/O not finished:
        //Mark PCB as blocked; mark it
        pcb->status = BLOCKED_PCB;

        //Turn CPU and scheduling switches on to schedule a new process
        CPU_SW = ON;
        SCHED_SW = ON;

        //Calculate active time for process and busy time for CPU
        time_type currTime = Clock;
        Diff_time(&pcb->run_time, &currTime);
        Add_time(&currTime, &(pcb->total_run_time));
        Add_time(&currTime, &(CPU.total_busy_time));

        //Record time process was blocked
        pcb->block_time = Clock;

        pcb->wait_rb = rb;

        //Print output message giving blocked procese and burst count
        print_out("\t\tUser %s is blocked for I/O.\n", pcb->username);
        print_out("\t\tCPU burst was %d instructions.\n\n",pcb->sjnburst);
    }

    //If rb has finished
    if(rb->status == DONE_RB)
    {
        //Delete it and keep running current process:
        //Delete rb from pcb's list
        Delete_rb(rb, pcb);

        //Turn CPU switch on to execute a process
        CPU_SW = ON;

        //Turn scheduling flag off to use current process (i.e., not schedule a new process)
        SCHED_SW = OFF;
    }
}
Example #2
0
/**
	Select the next process for allocation to the CPU according to the currently
	active scheduling algorithm.

	<b> Important Notes:</b>
	\li Currently, only first-come first-serve scheduling is being used.

	<b> Algorithm (FCFS): </b>
	\verbatim
		Update the number of processes serviced by the CPU

		Get first pcb in CPU's ready queue
		Remove first node in queue

		If not removing last pcb in ready queue
			Change head of ready queue

		Calculate time process was ready
		Increment total ready time for process
		Increment total CPU queue waiting time

		Reset the burst count for the next program
	\endverbatim

	@retval pcb -- next active process
 */
pcb_type*
Scheduler( )
{
    //Update the number of processes serviced by the CPU
    CPU.num_served++;

    if(CPU.ready_q == NULL) return NULL;

    //Get first pcb in CPU's ready queue
    pcb_list *pcbNode = CPU.ready_q;
    pcb_type *pcb = pcbNode->pcb;

    //Remove first node in queue
    CPU.ready_q = CPU.ready_q->next;

    //If not removing last pcb in ready queue
    if(pcbNode != CPU.ready_q)
    {
        //Change head of ready queue
        CPU.ready_q->prev->prev->next = CPU.ready_q;
        CPU.ready_q->prev = CPU.ready_q->prev->prev;
        //~ pcbNode->prev->next = pcbNode->next;
        //~ pcbNode->next->prev = pcbNode->prev;
        //~ CPU.ready_q = pcbNode->next;
    }
    else
    {
        CPU.ready_q = NULL;
    }

    pcb->run_time = Clock;

    //Calculate time process was ready
    time_type currTime = Clock;
    Diff_time(&pcb->ready_time, &currTime);

    //Increment total ready time for process
    Add_time(&currTime, &pcb->total_ready_time);

    //Increment total CPU queue waiting time
    Add_time(&currTime, &CPU.total_q_time);

    //Reset the burst count for the next program
    pcb->sjnburst = 0;

    //return next active process
    return pcb;
}
Example #3
0
/**
	Service an I/O interrupt event from a device.

	<b> Important Notes:</b>
	\li Events of type EIO_EVT will result in this call via
	Interrupt_Handler() in simulator.c after interrupt() in obj1.c sets the
	global Event variable.

	<b> Algorithm: </b>
	\verbatim
	Retrieve device that caused the I/O interrupt from the device table
	Retrieve request block issued to device that is now complete
	Retrieve process that issued the request block to the device

	Mark that device no longer has a current request block
	Mark rb's status as done

	Service next I/O request block for this device--call Start_IO()

	Determine current status of process
		If process is ready to be ran or is already running
			Turn off both switches

		If process is blocked due to I/O
			If process was waiting on this rb
				The process can now run:
					Delete rb from pcb's waiting list--call Delete_rb()
					Mark pcb as ready to run
					Add pcb to CPU's ready queue--call Add_cpuq()

			Record time process became ready
			Calculate and record time process was blocked

			If CPU is has no currently active process
				Turn on both switches to schedule a new process to run
			Otherwise, CPU is already busy, so
				Turn off both switches

		If the process is done, but only waiting for I/O to complete.

			Delete rb from pcb's waiting list--call Delete_rb()

			Load next program for pcb--call Next_pgm()
				If one was available
					Mark pcb's status as ready to run

			Record time process became ready
			Calculate and record time process was blocked

			If CPU is idle
				Turn on CPU and scheduling switches to run a new process
			Otherwise, CPU is already busy, so
				Turn off both switches

	\endverbatim
}
 */
void
Eio_Service( )
{
    //Retrieve device that caused the I/O interrupt from the device table
    device_type *device = &(Dev_Table[Agent-Num_Terminals-1]);

    //Retrieve request block issued to device that is now complete
    rb_type *currRB = device->current_rb;

    //Retrieve process that issued the request block to the device
    pcb_type *currPCB = currRB->pcb;

    //Mark that device no longer has a current request block
    device->current_rb = NULL;

    //Mark rb's status as done
    currRB->status = DONE_RB;

    //Service next I/O request block for this device--call Start_IO()
    Start_IO(Agent - Num_Terminals - 1);

    //Determine current status of process
    //If process is ready to be ran or is already running
    if(currPCB->status == READY_PCB || currPCB->status == ACTIVE_PCB)
    {
        //Turn off both switches
        CPU_SW = OFF;
        SCHED_SW = OFF;
    }

    //If process is blocked due to I/O
    else if(currPCB->status == BLOCKED_PCB)
    {
        //If process was waiting on this rb
        if(currPCB->wait_rb == currRB)
        {
            //The process can now run:
            //Delete rb from pcb's waiting list--call Delete_rb()
            Delete_rb(currRB, currPCB);

            //Mark pcb as ready to run
            currPCB->status = READY_PCB;

            currPCB->wait_rb = NULL;

            //Add pcb to CPU's ready queue--call Add_cpuq()
            Add_cpuq(currPCB);
        }

        //Record time process became ready
        currPCB->ready_time = Clock;

        //Calculate and record time process was blocked
        time_type currTime = Clock;
        Diff_time(&(currPCB->block_time), &currTime);
        Add_time(&currTime, &(currPCB->total_block_time));

        //If CPU is has no currently active process
        if(CPU.active_pcb == NULL)
        {
            //Turn on both switches to schedule a new process to run
            CPU_SW = ON;
            SCHED_SW = ON;
        }
        //Otherwise, CPU is already busy, so
        else
        {
            //Turn off both switches
            CPU_SW = OFF;
            SCHED_SW = OFF;
        }
    }

    //If the process is done, but only waiting for I/O to complete.
    if(currPCB->status == DONE_PCB)
    {
        //Delete rb from pcb's waiting list--call Delete_rb()
        Delete_rb(currRB,currPCB);

        //Load next program for pcb--call Next_pgm()
        //If one was available
        if(Next_pgm(currPCB) != 0)
        {
            //Mark pcb's status as ready to run
            currPCB->status == READY_PCB;
        }

        //Record time process became ready
        currPCB->ready_time = Clock;

        //Calculate and record time process was blocked
        time_type currTime = Clock;
        Diff_time(&(currPCB->block_time), &currTime);
        Add_time(&currTime, &(currPCB->total_block_time));

        //If CPU is idle
        if(CPU.active_pcb == NULL)
        {
            //Turn on CPU and scheduling switches to run a new process
            CPU_SW = ON;
            SCHED_SW = ON;
        }
        //Otherwise, CPU is already busy, so
        else
        {
            //Turn off both switches
            CPU_SW = OFF;
            SCHED_SW = OFF;
        }
    }
}
Example #4
0
/**
	Start processing I/O request for given device.

	<b> Important Notes:</b>
	\li Add_time() and Diff_time() should be used for calculating and
	incrementing many of the simulation time fields, such as the device's busy
	time.
	\li The time at which I/O completes is obtained using rb->bytes and the
	speed of the device: device->bytes_per_sec and device->nano_per_byte.
	Using all these values, the seconds and nanoseconds fields of a simulation
	time structure can be filled in. Note that bytes per sec and nano per byte
	are, to some degree, reciprocals, which implies that one should divide by
	the former but multiply with the latter.
	\li When finally adding the ending I/O event to the event list, the
	device's ID must be converted into an agent ID. Recall in objective 1 that
	agent IDs for devices were set to directly follow terminal user IDs.

	<b> Algorithm: </b>
	\verbatim
	If the device is busy
		Do nothing

	If the device has no requests to service in its waiting queue
		Do nothing

	Get and remove first node from waiting list

	Mark rb as the current request block in the device; set rb's status as active

	Update time spent waiting in queue--calculate difference between current time and time it was enqueued and add this value to the current queue wait time--use Add_time() and Diff_time()

	Compute length of time that I/O will take (transfer time)--compute number of seconds and then use the remainder to calculate the number of nanoseconds

	Increment device's busy time by the transfer time--use Add_time()

	Compute time I/O ends--ending time is the current time + transfer time--use Add_time() and Diff_time()

	Update the number of IO requests served by the device

	Add ending I/O device interrupt to event list
	\endverbatim

	@param[in] dev_id -- ID of device
	@retval None
 */
void
Start_IO( int dev_id )
{

    //If the device is busy
    if(Dev_Table[dev_id].current_rb != NULL && Dev_Table[dev_id].current_rb->status == ACTIVE_RB)
    {
        //Do nothing
        return;
    }

    //If the device has no requests to service in its waiting queue
    if(Dev_Table[dev_id].wait_q == NULL)
    {
        //Do nothing
        return;
    }

    rb_list *rbNode = Dev_Table[dev_id].wait_q;

    //Get and remove first node from waiting list
    Dev_Table[dev_id].wait_q->prev->next = Dev_Table[dev_id].wait_q->next;
    Dev_Table[dev_id].wait_q->next->prev = Dev_Table[dev_id].wait_q->prev;
    Dev_Table[dev_id].wait_q = rbNode->next;

    if(rbNode == Dev_Table[dev_id].wait_q)
    {
        Dev_Table[dev_id].wait_q = NULL;
    }

    rbNode->next = rbNode;
    rbNode->prev = rbNode;

    //Mark rb as the current request block in the device; set rb's status as active
    Dev_Table[dev_id].current_rb = rbNode->rb;
    rbNode->rb->status = ACTIVE_RB;

    //Update time spent waiting in queue--calculate difference between current time and time it was enqueued and add this value to the current queue wait time--use Add_time() and Diff_time()
    time_type currTime = Clock;
    Diff_time(&rbNode->rb->q_time, &currTime);
    Add_time(&currTime, &Dev_Table[dev_id].total_q_time);

    //Compute length of time that I/O will take (transfer time)--compute number of seconds and then use the remainder to calculate the number of nanoseconds
    time_type transferTime;
    transferTime.seconds = rbNode->rb->bytes / Dev_Table[dev_id].bytes_per_sec;
    transferTime.nanosec = (rbNode->rb->bytes % Dev_Table[dev_id].bytes_per_sec)*Dev_Table[dev_id].nano_per_byte;

    //Increment device's busy time by the transfer time--use Add_time()
    Add_time(&transferTime, &Dev_Table[dev_id].total_busy_time);

    //Compute time I/O ends--ending time is the current time + transfer time--use Add_time() and Diff_time()
    time_type ioEndTime = transferTime;
    Add_time(&Clock, &ioEndTime);

    //Update the number of IO requests served by the device
    Dev_Table[dev_id].num_served++;

    //Add ending I/O device interrupt to event list
    Add_Event(EIO_EVT,(dev_id+Num_Terminals+1),&ioEndTime);

}
Example #5
0
/**
	Start processing I/O request for given device.

	<b> Important Notes:</b>
	\li Add_time() and Diff_time() should be used for calculating and
	incrementing many of the simulation time fields, such as the device's busy
	time. 
	\li The time at which I/O completes is obtained using rb->bytes and the
	speed of the device: device->bytes_per_sec and device->nano_per_byte.
	Using all these values, the seconds and nanoseconds fields of a simulation
	time structure can be filled in. Note that bytes per sec and nano per byte
	are, to some degree, reciprocals, which implies that one should divide by
	the former but multiply with the latter.
	\li When finally adding the ending I/O event to the event list, the
	device's ID must be converted into an agent ID. Recall in objective 1 that
	agent IDs for devices were set to directly follow terminal user IDs.

	<b> Algorithm: </b>
	\verbatim
	If the device is busy
		Do nothing

	If the device has no requests to service in its waiting queue
		Do nothing

	Get and remove first node from waiting list

	Mark rb as the current request block in the device; set rb's status as active

	Update time spent waiting in queue--calculate difference between current time and time it was enqueued and add this value to the current queue wait time--use Add_time() and Diff_time()

	Compute length of time that I/O will take (transfer time)--compute number of seconds and then use the remainder to calculate the number of nanoseconds

	Increment device's busy time by the transfer time--use Add_time()

	Compute time I/O ends--ending time is the current time + transfer time--use Add_time() and Diff_time()

	Update the number of IO requests served by the device

	Add ending I/O device interrupt to event list
	\endverbatim

	@param[in] dev_id -- ID of device
	@retval None
 */
void
Start_IO( int dev_id )
{

	// DECLARE VARIABLES
	struct rb_list* rbListNode;
	struct rb_type* rb;
	struct time_type wait_time;
	struct time_type transfer_time;
	struct time_type eio_time;

	// TODO: verify discrepancy of: if(){ return } conditions

	// TODO: change to "==" (?)
	//If the device is busy
	if( Dev_Table[ dev_id ].current_rb != NULL ){
		//Do nothing
		return;
	}

	//If the device has no requests to service in its waiting queue
	if( Dev_Table[ dev_id ].wait_q == NULL ){
		//Do nothing
		return;
	}

	// TODO: revisit discrepancy
	//Get and remove first node from waiting list
	rbListNode = Dev_Table[ dev_id ].wait_q;

	// also store the node's request block to rb since we're freeing the node
	rb = rbListNode->rb;

	// does wait_q contain >1 node?
	if( rbListNode->next != rbListNode ){
		// wait_q contains more than 1 node
		rbListNode->prev->next = rbListNode->next;
		rbListNode->next->prev = rbListNode->prev;
		Dev_Table[ dev_id ].wait_q = Dev_Table[ dev_id ].wait_q->next;
	} else {
		// wait_q contains exactly 1 node
		rbListNode->next = NULL;
		rbListNode->prev = NULL;
		Dev_Table[ dev_id ].wait_q = NULL;
	}

	// TODO: free rbListNode (?)

	//Mark rb as the current request block in the device; set rb's status as active
	Dev_Table[ dev_id ].current_rb = rb;
	rb->status = ACTIVE_RB;

	// TODO: revisit discrepancy on all time calculations below

	//Update time spent waiting in queue--calculate difference between current time and time it was enqueued and add this value to the current queue wait time--use Add_time() and Diff_time()
	wait_time = Clock;
	Diff_time( &( rb->q_time ), &wait_time );
	Add_time( &wait_time, &( Dev_Table[ dev_id ].total_q_time ) );

	//Compute length of time that I/O will take (transfer time)--compute number of seconds and then use the remainder to calculate the number of nanoseconds
	transfer_time.seconds =   rb->bytes / Dev_Table[ dev_id ].bytes_per_sec;
	transfer_time.nanosec = ( rb->bytes % Dev_Table[ dev_id ].bytes_per_sec )
	 * Dev_Table[ dev_id ].nano_per_byte;

	//Increment device's busy time by the transfer time--use Add_time()
	Add_time( &transfer_time, &( Dev_Table[ dev_id ].total_busy_time ) );

	//Compute time I/O ends--ending time is the current time + transfer time--use Add_time() and Diff_time()
	eio_time = Clock;
	Add_time( &transfer_time, &eio_time );

	//Update the number of IO requests served by the device
	Dev_Table[ dev_id ].num_served = Dev_Table[ dev_id ].num_served + 1;

	//Add ending I/O device interrupt to event list
	Add_Event( EIO_EVT, dev_id + Num_Terminals + 1, &eio_time );

}
Example #6
0
/**
	Select the next process for allocation to the CPU according to the currently
	active scheduling algorithm.

	<b> Important Notes:</b>
	\li Currently, only first-come first-serve scheduling is being used.

	<b> Algorithm (FCFS): </b>
	\verbatim
		Update the number of processes serviced by the CPU

		Get first pcb in CPU's ready queue
		Remove first node in queue

		If not removing last pcb in ready queue
			Change head of ready queue

		Calculate time process was ready
		Increment total ready time for process
		Increment total CPU queue waiting time

		Reset the burst count for the next program
	\endverbatim

	@retval pcb -- next active process
 */
pcb_type*
Scheduler( )
{

	// DECLARE VARIABLES
	struct pcb_list* nextPcbListNode;
	struct pcb_type* nextPcb;
	struct time_type ready_time;

	//Update the number of processes serviced by the CPU
	CPU.num_served = CPU.num_served + 1;

	//Get first pcb in CPU's ready queue
	nextPcbListNode = CPU.ready_q;

	if( nextPcbListNode == NULL ){
		return NULL;
	}

	//Remove first node in queue
	nextPcb = nextPcbListNode->pcb;

	//If not removing last pcb in ready queue
	if( nextPcbListNode != nextPcbListNode->next ){

		//Change head of ready queue

		// set the last node's next ptr to the second node in the list
		CPU.ready_q->prev->next = CPU.ready_q->next;

		// set the second node in the list's prev ptr to the last node
		CPU.ready_q->next->prev = CPU.ready_q->prev;

		// set the head of the ready queue to be the second node in the list
		CPU.ready_q = CPU.ready_q->next;

	// TODO: add below else for "removing last pcb in ready queue" case
	} else {
		CPU.ready_q->next = NULL;
		CPU.ready_q->prev = NULL;
		CPU.ready_q = NULL;
	}

	// TODO: free(CPU.ready_q) here?

	//Calculate time process was ready

	//ready_time.seconds = 0;
	//ready_time.nanosec = 0;

	ready_time = Clock;
	nextPcb->run_time = Clock;

	Diff_time( &nextPcb->ready_time, &ready_time );
	//Diff_time( &Clock, &nextPcb->ready_time );

	//Increment total ready time for process
	Add_time( &ready_time, &nextPcb->total_ready_time );
	//Add_time( &nextPcb->ready_time, &nextPcb->total_ready_time );

	//Increment total CPU queue waiting time
	Add_time( &ready_time, &CPU.total_q_time );
	//Add_time( &nextPcb->ready_time, &CPU.total_q_time );

	//Reset the burst count for the next program
	nextPcb->sjnburst = 0;

	return nextPcb;
}
Example #7
0
/**
	Compute all simulation statistics and store them in the appropriate
	variables and data structures for display.  It is called by Clean_Up().

	<b> Algorithm: </b>
	\verbatim
	For each user
		Calculate total processing time
		Calculate total job blocked time
		Calculate total job wait time
		Calculate total job execution time
		Calculate efficiency for each process

	For each device
		Calculate response time for each device
		Calculate total idle time for each device
		Calculate utilization for each device

	Calculate average user execution time
	Calculate average user logon time
	Calculate average user blocked time
	Calculate average user wait time
	Calculate response time for CPU
	Calculate total idle time for CPU
	Calculate total utilization for CPU
	\endverbatim
 */
void
Calc_Stats( )
{

	// DECLARE VARIABLES
	struct time_type time; // throw-away/local var for calculations

	//For each user
	for( int i=0; i<Num_Terminals; i++ ){

		// skip if this user serviced 0 IORBs (nothing to calculate)
		if( Dev_Table[i].num_served == 0 ){
			continue;
		}

		//Calculate total processing time
		Add_time( &( Term_Table[i]->total_logon_time ), &Tot_Logon );

		//Calculate total job blocked time
		Add_time( &( Term_Table[i]->total_block_time ), &Tot_Block );

		//Calculate total job wait time
		Add_time( &( Term_Table[i]->total_ready_time ), &Tot_Wait );

		//Calculate total job execution time
		Add_time( &( Term_Table[i]->total_run_time ), &Tot_Run );

		//Calculate efficiency for each process

		time = Term_Table[i]->total_run_time;
		Add_time( &( Term_Table[i]->total_block_time ), &time );

		Term_Table[i]->efficiency =
		 Divide_time( &time, &( Term_Table[i]->total_logon_time ) )
		 * 100;

	}

	//For each device
	for( int i=0; i<Num_Devices; i++ ){

		//Calculate response time for each device
		time = Dev_Table[i].total_q_time;
		Add_time( &(Dev_Table[i].total_busy_time), &time );
		Average_time( &time, Dev_Table[i].num_served, &(Dev_Table[i].response) );

		//Calculate total idle time for each device
		time = Clock;
		Diff_time( &( Dev_Table[i].total_busy_time ), &time );
		Dev_Table[i].total_idle_time = time;

		//Calculate utilization for each device
		Dev_Table[i].utilize =
		 Divide_time( &( Dev_Table[i].total_busy_time ), &Clock )
		 * 100;

	}

	//Calculate average user execution time
	Average_time( &Tot_Run, Num_Terminals, &Avg_Run );

	//Calculate average user logon time
	Average_time( &Tot_Logon, Num_Terminals, &Avg_Logon );

	//Calculate average user blocked time
	Average_time( &Tot_Block, Num_Terminals, &Avg_Block );

	//Calculate average user wait time
	Average_time( &Tot_Wait, Num_Terminals, &Avg_Wait );

	//Calculate response time for CPU
	time = CPU.total_q_time;
	Add_time( &( CPU.total_busy_time ), &time );
	Average_time( &time, CPU.num_served, &( CPU.response ) );

	//Calculate total idle time for CPU
	time = Clock;
	Diff_time( &( CPU.total_busy_time ), &time );
	CPU.total_idle_time = time;

	//Calculate total utilization for CPU
	CPU.utilize =
	 Divide_time( &( CPU.total_busy_time ), &Clock )
	 * 100;

}