-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclasses.py
244 lines (216 loc) · 11.1 KB
/
classes.py
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
234
235
236
237
238
239
240
241
242
243
244
import logging
logging.basicConfig(filename = 'output.log', level=logging.DEBUG, format='%(message)s')
class PCB:
"""
Process Control Block class used to create new processes objects.
Defines a process containing:
`name`: String as process name
`pid`: Unique int as process ID
`priority`: int as process priority
`date_time`: datetime as process creation date and time
`remaining_time`: int representing the time units for the process to be finished
`next`: PCB process that comes after current PCB process in a queue
"""
def __init__(self, name, priority, date_time, init_address, end_address, remaining_time):
"""Initiate a new PCB process with values provided from processes_creator function."""
self.name = name
self.pid = id(self)
self.priority = priority
self.date_time = date_time
self.memory_address = [init_address, end_address]
self.remaining_time = remaining_time
self.next = None
def __repr__(self):
"""Formats the way we want to see a process and its attributes."""
return self.name + " | pid: " + str(self.pid) + " | priority: " + str(self.priority) + " | date_time: " + str(self.date_time) + " | memory_address: " + str(self.memory_address) + " | remaining_time: {:02d}".format(self.remaining_time)
class Queue:
"""
Circular queue of processes with same priority.
Creates a processes queue containing:
`first`: Current first PCB process in queue
`last`: Current last PCB process in queue (the one right before the first one)
`length`: int as quantity of processes currently in queue
`quantum`: int as the limit time units for a process in this Queue to use CPU
"""
def __init__(self, quantum):
"""
Initiate a new empty Queue with quantum value based
on priority of processes that will be in this queue.
"""
self.first = None
self.last = None
self.length = 0
self.quantum = quantum
def new_process(self, process):
"""
Adds a new PCB process to a Queue.
If a Queue is empty, it will be the first and last PCB process in it, pointing to itself as next,
otherwise it will assume the last position and point to the first PCB process as its next.
"""
if self.first == None:
self.first = self.last = process
self.first.next = self.last.next = process
else:
self.last.next = process
self.last = process
self.last.next = self.first
self.length += 1
def end_queue(self):
"""Frees Queue memory space when all PCB processes in it have been finished."""
del self
class OS:
"""
Operating system that manages processes Queues.
Creates an Operating system object containing:
`sorted_queues`: A list with all Queues with different priorities. This list is sorted by priority,
so in first list index will be the Queue of processes with highest priority,
and in the last list index will be the Queue of processes with lowest priority.
"""
def __init__(self):
"""Initiate a new Operating system with no processes Queue."""
self.sorted_queues = []
def __repr__(self):
"""Formats the way we want to see a OS and its Queues."""
description = ""
for queue in self.sorted_queues:
priority_queue = queue.first.priority
description += "QUEUE PRIORITY " + str(priority_queue) + " | " + str(queue.length) + " processes | quantum " + str(queue.quantum) + "\n"
process = queue.first
while True:
description += str(process) + "\n"
process = process.next
if process == queue.first:
break
return description
def queue_exists_in_sorted_queues(self, priority):
"""
Verifies if a Queue already exists in OS by comparing priority value.
Args:
`self`: OS
`priority`: priority to be searched in the OS Queues
Returns:
None if there is not a Queue of processes with this `priority` value in `OS`,
or returns the position of this Queue in the `OS` sorted list of Queues.
"""
for queue in self.sorted_queues:
if(queue.first.priority == priority):
return self.sorted_queues.index(queue)
return None
def new_position_in_sorted_queues(self, priority):
"""
If there is not a Queue of processes with a `priority` value in `OS`,
this method verifies where a new processes Queue must be inserted in order to
maintain the priority rule.
Args:
`self`: OS
`priority`: priority of the new Queue
Returns:
The position (index) where the new Queue must be inserted based on its `priority` value,
in order to maintain the priority rule.
"""
for queue in self.sorted_queues:
if(queue.first.priority < priority):
return self.sorted_queues.index(queue)
return len(self.sorted_queues)
def add_process(self, process):
"""
Receives a new PCB process and verifies if there is a Queue with same priority in OS.
If not, creates a new Queue and insert it in a position based on process
`priority` value, to maintain the priority rule.
If already exists a Queue with same priority in OS, then just adds it to the end of this Queue.
Args:
`self`: OS
`process`: PCB process object.
"""
queue_position = self.queue_exists_in_sorted_queues(process.priority)
if(queue_position is not None):
self.sorted_queues[queue_position].new_process(process)
else:
new_queue = Queue(quantum = process.priority)
new_queue.new_process(process)
new_position = self.new_position_in_sorted_queues(process.priority)
self.sorted_queues.insert(new_position, new_queue)
class Scheduler:
"""
Scheduler is responsible for managing which process will use CPU.
For it, Scheduler uses priority order and Round Robin rules.
"""
def context_switch(self, queue, running_process, time_unit):
"""
This method executes the Context Switch.
If the running process still has remaining time to be finished, then points to the next
process as the first in Queue, logs a message of Stopped process, Quantum ended and Context Switch event.
If the running process does not have remaining time to be finished, this means it has already been finished,
then points to the next process as the first in Queue, deletes the finished process memory space, decreases the
Queue length by 1, and logs a message of Ended process, Quantum ended and Context Switch event.
Args:
`self`: Scheduler
`queue`: current running priority Queue.
`running_process`: current running PCB process.
`time_unit`: the time unit that the context switch has happened.
Returns:
If next process is the same as a finished process itself, that means it was the last running
process, and the Queue finished all processes, returning `None` to indicate that there are no more
processes to be runned in this Queue.
Otherwise, returns the updated first process in `queue`, because Round Robin works in a circular Queue.
"""
if running_process.remaining_time:
queue.first = queue.first.next
queue.last = queue.last.next
logging.info("TIME {:03d} | Stopped ".format(time_unit) + str(running_process))
if queue.length > 1:
logging.info("TIME {:03d} | QUANTUM ENDED | CONTEXT SWITCH".format(time_unit))
else:
logging.info("TIME {:03d} | QUANTUM ENDED".format(time_unit))
else:
logging.info("TIME {:03d} | Ended ".format(time_unit) + str(running_process))
if queue.length > 1:
logging.info("TIME {:03d} | QUANTUM ENDED | CONTEXT SWITCH".format(time_unit))
else:
logging.info("TIME {:03d} | QUANTUM ENDED".format(time_unit))
aux = queue.first
queue.first = queue.first.next
queue.last.next = queue.first
queue.length -= 1
if queue.length == 0:
logging.info("TIME {:03d} | QUEUE PRIORITY ".format(time_unit) + str(queue.first.priority) + " ENDED | CONTEXT SWITCH\n")
del queue.first
del aux
queue.end_queue()
return None
del aux
return queue.first
def round_robin(self, time_unit, operating_system):
"""
This method simulates the Round Robin execution of processes.
Because the processes Queue are executed respecting their priority values,
a for loop is performed to iterate over the Queues in this `scheduler` list of Queues,
that was sorted by priority values, so the Queue with highest priority will be executed first.
For each Queue, the process to be executed will be the first in the Queue, and it can only use CPU
during the `runtime` value that is a copy from queue's `quantum` value.
The process currently using the CPU is the `running_process`.
For each time unit, the `runtime` for the process to use CPU decreases 1 unit, its `remaining_time`
also decreases 1 unit and the `time_unit` of processing increases 1 unit to simulate the real life time.
When the `runtime` is 0, that means the quantum of time for that process is finished, and a context switch
must happen in diffent ways:
- Gives the CPU to the next process in the same Queue, if there is another process;
- The same process continues in CPU, if it is the last process in this Queue and still has remaining time;
- Gives the CPU to the next highest priority Queue, if this process was the last process
in this Queue and it has no remaining time (has finished);
- Ends Round Robin scheduling, if this process has no remaning time and it is already in the lowest priority Queue (the last one).
Args:
`self`: Scheduler
`time_unit`: the time unit that the Round Robin is happening.
`operating_system`: OS with processes Queues.
"""
logging.info("ROUND ROBIN SCHEDULER EXECUTION:")
for queue in operating_system.sorted_queues:
running_process = queue.first
while running_process is not None:
runtime = queue.quantum
logging.info("TIME {:03d} | Executing ".format(time_unit) + str(running_process))
while runtime and running_process.remaining_time:
running_process.remaining_time -= 1
runtime -= 1
time_unit += 1
running_process = self.context_switch(queue, running_process, time_unit)