Pintos project for SUSTech
Pintos project_1 - for SUSTech OS
enum intr_level{INTR_OFF,INTR_ON};
enum intr_level Intr_enable(void);
enum intr_level intr_disable(void);
static int64_t record_blocked;
.timer_interrupt(struct intr_fame *args UNUSED)
int64_t record_blocked;
Struct thread{tid_t tid; enum thread_status status; char name[16]; unin8_t *stack; int priority; struct list_elem allelem; struct list_elem elem; uint32_t *pagedir; unsigned magic; int64_t record_blocked;}
Structure of thread for implementation of algorithm;
tid_t thread_create(const char *name, int priority, thread_func *, void *)
Create a new kernel thread named NAME.
void thread_block(void);
Put the current thread to sleep until the calling of thread_unblock;
void thread_unblock(struct thread *);
Put the thread into ready queue and keep on using it.
check_thread_status(struct thread *thre, void *aux UNUSED);
thread_unblock()
;record_blocked
to record how much time the thread has slept, so I add the function check_thread_status
to detect each thread’s status(blocked or not) and record_blocked’s time, if the conditions do not satisfy sleep time , it will call function thread_unblock
to arouse the thread and put it into ready queue to keep on executing.int64_t tricks_blocked
that record thread blocked time and add it in the struct thread
data structure to ensure that all threads have this property. Secondly, I add the new function check_thread_status to the thread_foreach
function to ensure that the judgment of the thread state and sleep time is performed for each thread. Third, if this function does not match the condition of the blocked time, it will be woken up and put back into the ready queue, which is also a guarantee of synchronization.int64_t record_blocked
is added to the thread structure to record how much time the thread has been sleeping. Then, the operating system’s own clock interrupt (each tick is executed once) is added to the thread state. Detect, subtract ticks_blocked by 1 for each test, and wake up this thread if it is reduced to zero.Struct thread{tid_t tid; enum thread_status status; char name[16]; unin8_t *stack; int priority; struct list_elem allelem; struct list_elem elem; uint32_t *pagedir; unsigned magic; int64_t record_blocked;}
struct list_elem elem;
int max_priority;
int rudimentary_priority;
struct list locks;
struct lock *lock_waiting;
struct list_elem{struct list_elem *prev; struct list_elem *next};
struct list {struct list_elem head; struct list_elem tail};
list_insert_ordered (&all_list, &t->allelem, (list_less_func *) &thread_cmp_priority, NULL);
void thread_set_priority(int new_priority);
void thread_yield(void);
void lock_acquire(struct lock *lock);
Step 1 : In the arousing execution of the thread, we need to call the thread_unblock
function, but the disadvantage of this function is that when the thread is placed into the ready list, there is no guarantee that the list will be sorted according to the priority order. When I look at the source code of list.c
, I found that the list_insert_ordered
function can be sorted in order of priority, so I need to change the original insert queue method to list_insert_ordered
.-`
Step 2 : When we want to reset the thread priority, we need to rethink the execution order of all threads, so we need to change the priority, we using function thread_set_priority
,and then throw the thread into the ready queue to continue execution, so we need to call the thread_yield
function, this is A good way to ensure that queues are executed in order of priority.
Step 3 :When a thread acquires a lock, if the thread with the lock has a lower priority than its own, it raises the priority of owning the lock. If the lock is locked by another lock, the priority is recursively given. After the thread releases the lock, the priority under the undone logic is restored, that is, the original priority of the thread.So I have to implement the thread_hold_the_lock
function and the thread_donate_priority
function, and encapsulate them in the lock_acqcuire function.When a thread is donated by multiple threads, change the current priority to the highest priority among the donation threads to ensure the rationality of this thread.
Step 4 :When a thread is prioritized, if the thread is in a donated state, the original priority of the thread is set, and then if the set priority is greater than the current priority, the current priority is changed, otherwise When the donation status is cancelled, the original priority is restored.So we need to implement a thread_update _priority
to implement this change process.
Step 5 :The remaining donated priority and current priority should be considered when releasing the lock to a lock priority.
Step 6 :The wait queue for the semaphore is implemented as a priority queue. Implement the condition’s waiters queue as a priority queue. And if the priority is changed when the lock is released, preemption can occur. In the cond_signal
function, use the list_sort
to sort the condition queue, Then implement the wait queue of the semaphore
as a priority queue.
thread_set_priority
, the strategy can be done because we have to read/write to the current thread’s priority that is updated every four tricks in the interrupt handler. Because of that reason, we can not use locks to solve it since the interrupt.int nice;
fixed_t recent_cpu;
Struct thread{tid_t tid; enum thread_status status; char name[16]; unin8_t *stack; int priority; struct list_elem allelem; struct list_elem elem; uint32_t *pagedir; unsigned magic; int64_t record_blocked;}
fixed_t load_avg;
The algorithm is still clear and easy to understand in the way of implementation.
step 1 : Implement floating point arithmetic logic in fixed_point.h
.
step 2 : Calculate the priority of the update thread in the timer_interrupt
for a period of time. Here, the system load_avg
and the recent_cpu
of all threads are updated every TIMER_FREQ
time.
step 3 : Every 4 timer_ticks
is updated once. Thread priority.
step 4 : Add one of the recent_cpu
of each timer_tick running thread.
step 5 : Although it is said that 64 priority queue scheduling is maintained, the essence is priority scheduling. We reserve the priority scheduling code written before, and remove the priority donation.
interrupts
, the other is to store int nice
and fixed_t recent_cpu
as variables in the struct thread
, to ensure that all threads share these two properties to ensure thread synchronization.
timer ticks R(A) R(B) R(C) P(A) P(B) P(C) thread to run
0 0 0 0 63 61 59 A
4 4 0 0 62 61 59 A
8 8 0 0 61 61 59 B
12 8 4 0 61 60 59 A
16 12 4 0 60 60 59 B
20 12 8 0 60 59 59 A
24 16 8 0 59 59 59 C
28 16 8 4 59 59 58 B
32 16 12 4 59 58 58 A
36 20 12 4 58 58 58 C
Yes, because if the threads have the same priority, it's unconspicuous to choose which threads to execute.
So, I choose two strategies to solve them:
1. If one of the threads in the ready list have the highest priority, it will execute first obviously;
2. If the threads in ready list have the same priority, then choose the thread has the most least run-time recently.