summaryrefslogtreecommitdiff
path: root/task-2-29994705.c
blob: 9e946bb914210ee8b61cef38f0931c2b565327ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
 * task-1-29994705.c
 * 
 * FIT2100 Assignment 2 for S2/2020
 * 
 * Alexander Occhipinti
 * Student ID: 29994705
 * Created: 14 Oct 2020 
 * Modified: 15 Oct 2020 
 * 
 * This is the source file for Task 1 of Assignment 2.
 * This program simulates a FCFS (first-come-first-serve) scheduler with at most 10 simulated processes.
 * These processes are read from file, simulated, then results are exported to "process-data.txt".
 * These results include wait time, turnaround time and whether the deadline was met for each process.
 * The purpose of this is to compare various scheduling algorithms.
 *  
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "task-2-29994705.h"

// NOTE: the structs provided in the assignment document are in the header file

/*
 * Compares two pcb_t structs to determine the order between the two in a FCFS system.
 *  This function is designed to interface with C's qsort function.
 * 
 * Args:
 *  pcb_a: any given process' PCB as a pcb_t
 *  pcb_b: any given process' PCB as a pcb_t
 * 
 * Returns (int):
 *  -1 if pcb_a comes before b
 *  0 if both equal
 *  1 if pcb_a comes after b
 * 
*/
int fcfs_compare(const void *pcb_a, const void *pcb_b) {
    pcb_t *pcb_x = (pcb_t *) pcb_a;
    pcb_t *pcb_y = (pcb_t *) pcb_b;
    return pcb_x->entryTime - pcb_y->entryTime;
}

/*
 * Reads and interprets a file containing process PCBs to simulate in the given format:
 *  [Process Name] [Arrival Time] [Service Time] [Deadline]
 *  separated by spaces.
 * Writes these PCBs to a given array via a pointer to be simulated later.
 * 
 * Args:
 *  file_path: the path to the input file
 *  pcb_array: an array to write PCBs into
 *  array_size: the size of the given PCB array
 * 
 * Returns (int):
 *  The number of PCBs actually read from the file. (May be less than the maximum array size)
 * 
*/
int pcbs_from_file(char* file_path, pcb_t *pcb_array, int array_size) {
    FILE *fp;
    fp = fopen(file_path, "r");
    int i;

    /* Read PCBs in from file until array is full or the file ends */
    for (i = 0; i<array_size && !feof(fp); i++){
        pcb_t process;
        fscanf(fp, "%s %d %d %d", process.process_name, &process.entryTime, &process.serviceTime, &process.deadline);
        /* Initialise values in the struct */
        process.state = UNENTERED;
        process.remainingTime = process.serviceTime;
        pcb_array[i] = process;
    }
    fclose(fp);
    return i;
}

/* 
 * Calculates and exports results to the default file path after a simulation, provided a list of PCBs.
 *  Writes to a file information in the following format:
 *  [Process Name] [Wait Time] [Turnaround Time] [Deadline Met]
 *  separated by spaces.
 * 
 * Args:
 *  pcb_array: an array of process PCBs after a simulation (i.e. experimental data filled into each struct)
 *  num_pcbs: the number of PCBs in the array provided
 * 
 * Returns void
 * 
*/
void export_results(pcb_t *pcb_array, int num_pcbs) {
    FILE *fp;
    fp = fopen(RESULT_FILE_PATH, "w");
    for (int i = 0; i < num_pcbs; i++) {
        int wait_time, turnaround_time, met_deadline;
        wait_time = pcb_array[i].timeStarted - pcb_array[i].entryTime;
        turnaround_time = pcb_array[i].timeFinished - pcb_array[i].entryTime;
        met_deadline = turnaround_time <= pcb_array[i].deadline;
        fprintf(fp, "%s %d %d %d\n", pcb_array[i].process_name, wait_time, turnaround_time, met_deadline);
    }
    fclose(fp);
}

/*
 * The function that runs the main FCFS scheduling simulation loop, provided an array of process PCBs to simulate.
 *  Each loop represents a single unit of time, where processes may either be become ready, start running or finish.
 *  For each unit of time, the following happens (in order):
 *   Each process is checked to see if it is ready (i.e. simulating the user or system starting a new process)
 *   The current running process is checked to see if it has finished executing
 *   The next process in the array (determined by FCFS) is checked to see if it is able to be run (i.e. no other process running)
 *   The scheduler sleeps for a short amount of time (one second by default) and the simulated time is incremeneted by one unit
 *  The simulation terminates once every process has been completed, then the results are exported. 
 * 
 * Args:
 *  pcb_array: An array of process PCB structs to run the simulation on
 *  num_pcbs: the number of PCBs in the provided array
 * 
 * Returns void
*/

int find_next_process(pcb_t *pcb_array, int num_pcbs, int *cursor, pcb_t **this_process) {
    int i;
    int started_from = *cursor;
    for (i = 0; i<num_pcbs; i++) {
        *cursor = (started_from + i) % num_pcbs;
        // printf("(%d,%d)", *cursor, num_pcbs);
        // printf("[%d]", pcb_array[*cursor].state);
        // printf("{%d}", i);

        if (pcb_array[*cursor].state == READY) {
            *this_process = &pcb_array[*cursor];
            return 1;
        }
    }
    return 0;
}


int scan_ready_procs(pcb_t *pcb_array, int num_pcbs, int time){
    int total_ready = 0;
    for (int i = 0; i < num_pcbs; i++) {
        if (pcb_array[i].entryTime <= time && pcb_array[i].state == UNENTERED) {
            pcb_array[i].state = READY;
            total_ready++;
            printf("Time %d: Process %s entered the system.\n", time, pcb_array[i].process_name);
        }
    }
    return total_ready;
}

void perform_fcfs_scheduling(pcb_t *pcb_array, int num_pcbs) {

    qsort(pcb_array, num_pcbs, sizeof(pcb_t), fcfs_compare); // Sort the PCB array in the order determined by the FCFS algorithm (via comparison function)
    int time = 0;
    int cursor = 0;
    int procs_left = num_pcbs;
    pcb_t *this_process = NULL;

    // for (int i = 0; i < num_pcbs; i++) {
    //     if (pcb_array[i].entryTime <= time && pcb_array[i].state == UNENTERED) {
    //         pcb_array[i].state = READY;
    //         printf("Time %d: Process %s entered the system.\n", time, pcb_array[i].process_name);
    //     }
    // }

    // this_process = find_next_process(pcb_array, num_pcbs, &cursor, &finished_flag);

    while (procs_left > 0) { // Loop until all processes are finished

        
        int num_procs_ready = scan_ready_procs(pcb_array, num_pcbs, time);

        if (!this_process)
            find_next_process(pcb_array, num_pcbs, &cursor, &this_process);

        if (this_process) {
            /* Checks if the current process has completed, sets its status to EXIT if so */
            if (this_process->state == RUNNING) {
                // printf("Time %d: Process %s is running.\n", time, this_process->process_name);
                if (--this_process->remainingTime == 0) { // Is there no remaining time left?
                    this_process->state = EXIT;
                    this_process->timeFinished = time;
                    printf("Time %d: Process %s has finished executing.\n", time, this_process->process_name);
                    procs_left--;
                    cursor++;
                    find_next_process(pcb_array, num_pcbs, &cursor, &this_process);
                    if (procs_left < 0) break;
                } else if ((this_process->serviceTime-this_process->remainingTime) % 2 == 0) {
                    this_process->state = READY;
                    cursor++;
                    find_next_process(pcb_array, num_pcbs, &cursor, &this_process);
                }
            }

            /* Checks if the current process ready for execution and sets its status to RUNNING */
            if (this_process->state == READY) {
                this_process->state = RUNNING;
                this_process->timeStarted = time;
                printf("Time %d: Process %s is now running.\n", time, this_process->process_name);
            }

        } else {
            printf("Time %d: CPU is idle.\n", time);
        }

        // Sleep and continue to the next unit of time
        sleep(WAIT_PER_CYCLE);
        time++;
    }

    export_results(pcb_array, num_pcbs);
}


/*
 * The Main function, called when the program is started
 * Reads PCBs from file, simulates the scheduler on them, then exports the results.
 * 
 * Interprets the following program arguments:
 *  0 args: runs with the DEFAULT_INPUT_PATH as the input file
 *  1 arg: (a path to a text file): exports the results to the given path 
*/
int main(int argc, char *argv[]) {
    char in_path[MAX_PATH_SIZE] = DEFAULT_INPUT_PATH; // Initialise the PCB input file path to the default one
    if (argc > 1) strncpy(in_path, argv[1], MAX_PATH_SIZE); // Set the input path to the first argument if it was provided

    pcb_t pcb_array[MAX_NUM_PROCS]; // Init the array that will hold the PCBs
    int num_pcbs = pcbs_from_file(in_path, pcb_array, MAX_NUM_PROCS); // Read the PCBs from the provided file path, into the PCB array
    perform_fcfs_scheduling(pcb_array, num_pcbs); // Run the simulation on the read-in PCB array
    return 0;
}