Compare commits

...

213 Commits

Author SHA1 Message Date
Themis Demetriades
e1489e5244 Fix Bug: Free all entries in the fd hashtable when the process exits, (Saleh, Ethan) 2024-11-13 21:57:41 +00:00
Themis Demetriades
785f1a8d62 Fix child_results loop accessing after free() 2024-11-13 21:52:38 +00:00
Themis Demetriades
1368fa144a Add debugging messages for probing multi-oom test case 2024-11-13 18:57:02 +00:00
Themis Demetriades
446c50ea29 Remove superfluous process_exit () in start_process 2024-11-13 17:58:34 +00:00
Themis Demetriades
be68d81cf6 Fix memory leak in start_process due to not freeing proc_start_data when success in initializing stack 2024-11-13 17:21:42 +00:00
8bcd0a467c Tidy up a69b9c808e 2024-11-12 21:35:41 +00:00
dd979f34c8 Fix syn-read, syn-write, and always free elements from donors_list 2024-11-12 21:30:23 +00:00
ca9d23edf9 Always release filesys_lock when checking if file is valid in process_execute 2024-11-12 20:07:51 +00:00
Themis Demetriades
d878dbc132 Fix bug in userprog-merge where file writes were denied in the wrong thread 2024-11-12 16:22:32 +00:00
Themis Demetriades
b0400693ae Update process_execute to acquire lock when checking if file exists 2024-11-12 16:12:24 +00:00
Themis Demetriades
3418425f20 Don't acquire filesys lock when calling exec 2024-11-12 16:08:27 +00:00
Themis Demetriades
a69b9c808e Update start_process to acquire filesys lock when loading user process file 2024-11-12 14:21:33 +00:00
Demetriades, Themis
7d9900c6d8 Merge branch 'exec-missing-validation' into 'master'
Add validation to check for missing files in exec() args

See merge request lab2425_autumn/pintos_22!36
2024-11-11 23:25:26 +00:00
EDiasAlberto
72afecfbda Add validation to check for missing files in exec() args 2024-11-11 23:10:02 +00:00
Demetriades, Themis
9e692ced9e Merge branch 'userprog-merge' into 'master'
Update variable references between conflicting merges to refer to the same...

See merge request lab2425_autumn/pintos_22!35
2024-11-11 22:59:45 +00:00
Demetriades, Themis
f194fa1fa8 Merge branch 'userprog-merge' into 'master'
Implement complete stack initialization, process_wait, and all system calls correctly except exec

See merge request lab2425_autumn/pintos_22!34
2024-11-11 22:56:28 +00:00
Themis Demetriades
e8713e26c6 Update variable references between conflicting merges to refer to the same data in stack initialization 2024-11-11 22:51:19 +00:00
Demetriades, Themis
47c3657b65 Merge branch 'process-wait' into 'userprog-merge'
Fix race-condition in process result (memory leak), fix infinite loop in donors_list

See merge request lab2425_autumn/pintos_22!33
2024-11-11 22:24:39 +00:00
Demetriades, Themis
4eafdf10ba Merge branch 'read-only-exec' into 'userprog-merge'
Combine syscall code with final stack initialization code

See merge request lab2425_autumn/pintos_22!32
2024-11-11 22:23:20 +00:00
Themis Demetriades
14a4841772 Fix bug where size of file name buffer was less than maximum file name size 2024-11-11 22:13:10 +00:00
Themis Demetriades
52fdd47e0c Fix race condition in the passing of data from thread executing process_execute to its child 2024-11-11 21:51:38 +00:00
Themis Demetriades
049fc5559c Reformat stack initialization code to follow style for length and spacing 2024-11-11 21:20:53 +00:00
EDiasAlberto
98a5818406 add file_deny_write and file_allow_write to process creation and exiting to make executable read-only 2024-11-11 17:55:24 +00:00
18c884234d Fix race-condition in process result (memory leak), fix infinite loop in donors_list 2024-11-11 17:35:49 +00:00
EDiasAlberto
5b1e4b561b merge process-wait 2024-11-11 15:50:21 +00:00
Themis Demetriades
b8d358ecb2 Update stack initialization to handle overflow by allocating a second page for argument pointers 2024-11-11 13:13:21 +00:00
Themis Demetriades
8b2fc86b51 Refactor process_init_stack to reduce code duplication 2024-11-10 14:34:38 +00:00
Themis Demetriades
324301e7b3 Update process_execute function comment to reflect new function arguments 2024-11-10 13:43:22 +00:00
Themis Demetriades
0ac46db2e4 Refactor process initialization to obtain name of process file in process_execute 2024-11-10 13:41:08 +00:00
Themis Demetriades
795d81b7ad Update process_init_tack saveptr argument name to cmd_savetpr for clarity 2024-11-10 11:33:29 +00:00
Themis Demetriades
6018c0f6ec Refactor process_init_stack to not surpass 80 character limit 2024-11-10 11:25:20 +00:00
Themis Demetriades
f0dae74cf3 Update stack initialization alignment calculation to use WORD_SIZE constant for clarity 2024-11-10 11:24:51 +00:00
Themis Demetriades
a165107f5f Update process execution related function argument names to be more accurate 2024-11-10 11:17:22 +00:00
Themis Demetriades
b37f205334 Update process_init_stack to return success, refactoring error handling to occur inside start_process 2024-11-10 11:13:11 +00:00
EDiasAlberto
1bfd73b202 Comment syscall functions and handlers 2024-11-10 01:32:58 +00:00
7778e05aa4 Fix deadlock by release of lock and semaphore in the wrong order 2024-11-09 11:06:36 +00:00
sBubshait
115c650c55 Fix Bug in thread initialisation: only init hash if USERPROG is defined 2024-11-08 19:10:42 +00:00
Themis Demetriades
5ed999bc9c Refactor push_to_stack helper to match style of other helper functions 2024-11-08 16:53:30 +00:00
sBubshait
e40794e672 Fix Bug in fd_get_file: In case fd not found, then returns NULL, w/ E 2024-11-08 16:48:19 +00:00
Themis Demetriades
a19f02fad7 Move user process stack initialization into a helper function 2024-11-08 16:46:18 +00:00
sBubshait
8912ef4660 Implement writing to file system files in the write system call, w/ E 2024-11-08 16:26:47 +00:00
EDiasAlberto
18694d7b62 Implement file reading syscall and fix fd validation w/ S. 2024-11-08 16:25:49 +00:00
sBubshait
2a1cc3c361 Implement filesize and tell system calls, w/ E 2024-11-08 16:14:53 +00:00
EDiasAlberto
3cfbe198e0 Implement syscall for seek w/ S. 2024-11-08 16:10:46 +00:00
EDiasAlberto
75bd3fbde0 Implement syscall for close() and fix typing bug in fd_get_file w/ S. 2024-11-08 16:02:51 +00:00
Themis Demetriades
b866fa88cd Refactor process.c to use FNAME_MAX_LEN constant 2024-11-08 15:54:47 +00:00
sBubshait
a80084e907 Fix Bug in fd_get_file declaration use open_file instead of file, w/ E 2024-11-08 15:54:28 +00:00
sBubshait
5424276603 Add a helper function to get a file from its descriptor (FD), w/ E 2024-11-08 15:50:48 +00:00
Themis Demetriades
b64434fb9d Move definition of maximum file name length from <syscall> to file.h 2024-11-08 15:41:35 +00:00
EDiasAlberto
92e93b8060 Implement syscall for file opening and refactor open_files initialisation in thread.c w/ S. 2024-11-08 15:33:47 +00:00
sBubshait
5bd94894e0 Update thread structure to add a hash table of open files and initialise it, w/ E 2024-11-08 15:13:04 +00:00
sBubshait
5bbe7a03c0 Add in syscall hash helper functions for open_file struct: fd_hash and fd_less, w/ E 2024-11-08 15:12:08 +00:00
sBubshait
b112824a64 Implement the exec system call through process_execute, w/ E 2024-11-08 14:41:27 +00:00
EDiasAlberto
dca9d8f5a3 Implement syscall for file removal w/ S. 2024-11-08 14:35:23 +00:00
EDiasAlberto
a8676f2e09 Implement syscall for file creation, with relevant locks w/ S. 2024-11-08 14:34:08 +00:00
sBubshait
26e38be761 Update validate_user_pointer to check if the ptr is mapped to a physical memory address, w/ E 2024-11-08 14:21:16 +00:00
6ed1ccd21e Fix process_result locking by acquiring in process_wait as well to prevent freeing memory too early 2024-11-08 10:50:10 +00:00
84fe637c7e Remove process_result lock since it is an invalid solution
TODO : synchronise process_result in another way
2024-11-08 09:16:18 +00:00
d95894085b Implement syscall_exec via process_execute 2024-11-08 09:15:22 +00:00
fde70dcf59 Implement process_wait. 2024-11-08 03:56:15 +00:00
7349b4e66f Fix typo list_insert -> list_push_back for thread.child_results 2024-11-08 03:55:34 +00:00
ec8547aec9 Implement creation of process results 2024-11-08 03:31:48 +00:00
ddcd59fdf8 Add child and own process result information to struct thread 2024-11-08 03:30:58 +00:00
Demetriades, Themis
ed09e0b08e Merge branch 'system-calls' into 'master'
Add support for some basic system calls and args handling correctly.

See merge request lab2425_autumn/pintos_22!29
2024-11-07 19:36:29 +00:00
sBubshait
39018419cd Fix Bug in Process.c to print the exit statement in process_exit instead of exit syscall to handle unexpected/bad exits 2024-11-07 12:15:29 +00:00
sBubshait
6a3cf67d33 Fix Bug in process.c initialising a char to NULL 2024-11-07 11:55:26 +00:00
sBubshait
2bfde66d22 Merge remote-tracking branch 'origin/user-programs-stdout' into system-calls 2024-11-07 11:45:36 +00:00
Themis Demetriades
273fb48b31 Fix stack initialization to pass stack addreses (rather than thread addresses) for the arguments and only pass name a single time 2024-11-07 00:40:52 +00:00
Themis Demetriades
26ae7ac02e Fix bug in stack creation which would count one extra argument for argc 2024-11-06 23:57:48 +00:00
Themis Demetriades
1ca9d09512 Update exit () syscall to print correct termination message 2024-11-06 23:01:10 +00:00
Themis Demetriades
b2764cfa0c Revert setup_stack pointer decrement 'hack' faking stack initialization 2024-11-06 22:46:11 +00:00
Themis Demetriades
4020a140d2 Fix removal of 'timer.h' include needed for calling timer_sleep in process module 2024-11-06 22:36:43 +00:00
Demetriades, Themis
87dd84a9b9 Merge branch 'user-programs' into 'user-programs-stdout'
Merge basic system calls with stack set-up infrastructure

See merge request lab2425_autumn/pintos_22!27
2024-11-06 22:21:28 +00:00
Demetriades, Themis
014642c789 Merge branch 'user-programs-stdout' into 'user-programs'
# Conflicts:
#   src/userprog/process.c
2024-11-06 22:20:48 +00:00
Dias Alberto, Ethan
dfa42b9d25 Merge branch 'user-programs' into 'system-calls'
Implement fake stack and temporary timer change

See merge request lab2425_autumn/pintos_22!26
2024-11-06 16:57:10 +00:00
Dias Alberto, Ethan
1a4afc9ec7 Merge branch 'system-calls' into 'user-programs'
# Conflicts:
#   src/userprog/process.c

Fix conflict in differing placeholder process_wait implementations
2024-11-06 16:56:43 +00:00
Dias Alberto, Ethan
5535cbae24 Merge branch 'master' into 'system-calls'
Merge refactor and removal of duplicated code into section-specific branch

See merge request lab2425_autumn/pintos_22!25
2024-11-06 16:45:53 +00:00
Dias Alberto, Ethan
f685086d05 Merge branch 'refactor-semaphore' into 'master'
Refactor synch.c to remove code duplication in lock release

See merge request lab2425_autumn/pintos_22!23
2024-11-06 16:42:26 +00:00
Dias Alberto, Ethan
2fefdef605 Merge branch 'user-programs-temporary-fix' into 'user-programs'
Temporary fixes for process waiting and stack setup to allow simple user programs to run

See merge request lab2425_autumn/pintos_22!24
2024-11-06 16:41:49 +00:00
sBubshait
fcb7e9e441 Update setup_stack to temporarily fake set-up for a stack to prevent page faults in no arg user programs 2024-11-06 15:48:27 +00:00
sBubshait
ab716de0a6 Update process_wait to temporarily sleep for 1 second to allow user programs to run 2024-11-06 15:46:47 +00:00
sBubshait
91cef4d650 Refactor lock release and sema up to remove unnecessary code 2024-11-06 15:36:56 +00:00
Themis Demetriades
b0c1923d44 Update stack argument initialization behaviour to terminate creation of process on failing memory allocations 2024-11-06 12:28:58 +00:00
sBubshait
5aac37d167 Add temporary fixes to process_wait and setup stack, w/ E 2024-11-05 23:28:17 +00:00
sBubshait
02fff62ca2 Refactor syscall.c to follow PintOS styling, w/ E 2024-11-05 23:24:41 +00:00
EDiasAlberto
f4290c31f3 Implement syscall_read for console input w/ S. 2024-11-05 23:20:18 +00:00
sBubshait
01933cb5de Implement the write system call, w/ E 2024-11-05 23:07:07 +00:00
EDiasAlberto
b3e23eb1cc Implement system call wait w/ S. 2024-11-05 22:48:35 +00:00
EDiasAlberto
421f2c1206 Refactor function names and includes in syscall.c to avoid conflicts w/ S. 2024-11-05 22:46:21 +00:00
sBubshait
e9c4061531 Implement the exit system call, w/ E 2024-11-05 22:38:45 +00:00
sBubshait
2dccd87a76 Update thread to add exit_status, intialised to -1, into the thread structure, w/ E 2024-11-05 22:38:09 +00:00
Themis Demetriades
b4c41b0a6a Remove superfluous include in process.c 2024-11-04 12:57:29 +00:00
Themis Demetriades
2a890d5bd2 Reformat calculation of padding size in stack set-up to increase clarity 2024-11-04 12:54:18 +00:00
Themis Demetriades
6c6ce77824 Implement complete stack initialization for user processes, without accounting for overflow 2024-11-04 01:11:19 +00:00
EDiasAlberto
f8e529e877 Add UNUSED tag to system call function skeletons w/ S. 2024-11-04 01:02:04 +00:00
EDiasAlberto
2a9ab5ec97 fix merge conflicts 2024-11-04 01:00:33 +00:00
EDiasAlberto
4c27aa0203 Complete syscall lookup table, and syscall stubs and skeletons w/ S. 2024-11-04 00:57:19 +00:00
sBubshait
5e2342fad7 Update syscall to make syscall_number an unsigned integer instead of an int 2024-11-04 00:49:47 +00:00
sBubshait
0d057da3dc Refactor syscall to follow PintOS style in adding space after after function name in calls 2024-11-04 00:48:36 +00:00
sBubshait
79f6a8e808 Fix Bug in syscall handler related to pointer arithmetic: add sizeof uintptr_t instead of 1 2024-11-04 00:44:55 +00:00
sBubshait
3a258cf064 Update validate_user_pointer to perform no memory checks when size is 0 2024-11-04 00:38:58 +00:00
sBubshait
e718159ed8 Update syscall to use screaming uppercase casing for a constant 2024-11-04 00:29:07 +00:00
sBubshait
ade8faf0f4 Update syscall to add more comments explaining the basic handler 2024-11-04 00:20:09 +00:00
Themis Demetriades
6f9c911ebe Update start_process to pad process stack before pushing argv elements for performance 2024-11-04 00:19:36 +00:00
Themis Demetriades
62d2cb54e5 Update start_process to push pointers to process arguments onto the process thread's stack 2024-11-04 00:16:04 +00:00
Themis Demetriades
92c681ff02 Reformat start_process stack initialization code to follow style 2024-11-04 00:04:08 +00:00
Themis Demetriades
34d6c15d73 Update start_process to push process argument values to its thread's stack 2024-11-04 00:00:56 +00:00
sBubshait
d626b7a392 Implement basic syscall_handler using the lookup table, w/ E 2024-11-03 23:47:22 +00:00
EDiasAlberto
87126237ad Implement function to validate user memory pointers w/ S. 2024-11-03 23:20:42 +00:00
sBubshait
62453ef432 Add a look up table from system call numbers to their handler functions, w/ E 2024-11-03 22:54:03 +00:00
EDiasAlberto
c0f85a6bcc Implement skeleton for exit command w/ S. 2024-11-03 22:36:22 +00:00
EDiasAlberto
fa6dac2108 Implement halt system call w/ S. 2024-11-03 22:34:50 +00:00
sBubshait
0bf5fdb0e5 Add syscall_function type definition for the different syscall handlers, w/ E 2024-11-03 22:28:17 +00:00
Themis Demetriades
e26b11cce6 Update setup_stack skeleton to fake minimal stack set-up for testing purposes 2024-11-03 18:04:42 +00:00
Themis Demetriades
eb458efa59 Update process_wait skeleton to loop infinitely for testing purposes 2024-11-03 17:38:38 +00:00
Saleh Bubshait
74efa5e652 Merge branch 'task1/themis/thread-create-refactor' into 'master'
Refactor thread_create () to call thread_get_priority in order to retrieve priority

See merge request lab2425_autumn/pintos_22!21
2024-10-25 16:56:31 +00:00
Themis Demetriades
4066354f8a Refactor thread_create () to call thread_get_priority in order to retrieve priority 2024-10-25 17:03:18 +01:00
Demetriades, Themis
4c33653369 Merge branch 'task1/saleh/priority-donation-refactoring' into 'master'
priority donation refactoring

See merge request lab2425_autumn/pintos_22!20
2024-10-25 15:57:22 +00:00
sBubshait
c2414ec54d Add priority_less for comparing threads based on priority and Refactor sema up to use list_max for clarity 2024-10-25 16:04:55 +01:00
sBubshait
30ab3ae861 Update thread_create to only yield CPU to the new thread if necessary 2024-10-25 15:47:19 +01:00
sBubshait
81309dcda9 Refactor sema_up to follow PintOS styling of if statements 2024-10-25 15:44:56 +01:00
Demetriades, Themis
bbb62d2ee7 Merge branch 'gleb/fix-non-busy-sleep-bug' into 'master'
Remove unnecassary inter_disable from non-busy sleep

See merge request lab2425_autumn/pintos_22!19
2024-10-25 14:19:36 +00:00
Demetriades, Themis
f8bdd30f09 Merge branch 'gleb/bsd-priority-donation' into 'master'
Allow priority donation with BSD scheduler.

See merge request lab2425_autumn/pintos_22!18
2024-10-25 14:19:18 +00:00
869571108d Remove from sleeping_threads immediately, instead of when thread is scheduled 2024-10-25 14:10:59 +01:00
6e072a557f Allow priority donation with BSD scheduler.
- Recalculate priority with donations after BSD priority updates
- Remove mlfqs checks from lock_acquire
2024-10-25 14:03:55 +01:00
Saleh Bubshait
af31968e67 Merge branch 'gleb/fix-bsd-ready-list-order' into 'master'
Re-sort ready_list when priorities update + PRI_UPDATE_FREQ

See merge request lab2425_autumn/pintos_22!17
2024-10-25 08:32:08 +00:00
093c6efd30 Re-sort ready_list when priorities update + PRI_UPDATE_FREQ
- PRI_UPDATE_FREQ is a separate value from TIME_SLICE, they just
  happen to be set the same.
2024-10-25 09:13:27 +01:00
sb3923
c4cefbc2d5 Merge branch 'merged-complete' into 'master'
Merge code refactors into main branch

See merge request lab2425_autumn/pintos_22!16
2024-10-24 22:15:24 +00:00
EDiasAlberto
b88baede64 rename recent_cpu update function 2024-10-24 22:45:49 +01:00
EDiasAlberto
7d196ffc57 adjust spacing around brackets to fit styling conventions 2024-10-24 22:30:38 +01:00
EDiasAlberto
39f4edd5e6 remove unnecessary curly brace in thread_tick () 2024-10-24 22:09:28 +01:00
EDiasAlberto
60acc2e58d refactor thread_mlfqs checks in synch.c 2024-10-24 21:46:39 +01:00
EDiasAlberto
a6a0c4ad25 remove empty lines in thread.c 2024-10-24 20:47:30 +01:00
EDiasAlberto
74657bbf9c reorder init_thread for clarity 2024-10-24 20:34:44 +01:00
EDiasAlberto
0b230131f1 refactor thread struct to keep all changes together 2024-10-24 20:17:39 +01:00
EDiasAlberto
49a9284f02 refactor fixed-point.h to match style standards 2024-10-24 20:16:40 +01:00
Dias Alberto, Ethan
e749936b1f Merge branch 'merged-complete' into 'master'
Merge complete code base into master

See merge request lab2425_autumn/pintos_22!15
2024-10-23 19:05:45 +00:00
EDiasAlberto
20be75748c replace thread_mlfqs checks in priority calculations with assertions 2024-10-23 19:57:17 +01:00
EDiasAlberto
d8c843f16a add mlfqs condition checking in synchronisation primitives 2024-10-23 19:37:27 +01:00
EDiasAlberto
1fb101c365 remove repeated comparator and reorder thread_init operations 2024-10-23 18:56:48 +01:00
EDiasAlberto
c357126cc1 fix missing curly brace 2024-10-23 17:51:48 +01:00
Dias Alberto, Ethan
176750282f Merge branch 'BSD-merged' into 'master'
BSD Scheduler implementation

See merge request lab2425_autumn/pintos_22!9
2024-10-23 16:34:39 +00:00
Dias Alberto, Ethan
ed11c61f87 Merge branch 'master' into 'BSD-merged', fixing merge conflicts
# Conflicts:
#   src/threads/thread.c
2024-10-23 16:30:24 +00:00
Dias Alberto, Ethan
0cbae2a2e5 Merge branch 'task1/priority-donation' into 'master'
Merge 'task1/priority-donation' into 'master'

See merge request lab2425_autumn/pintos_22!14
2024-10-23 16:15:44 +00:00
Themis Demetriades
95386971e2 Update lock_release to disable interrupts in critical sections w/ S 2024-10-23 16:45:42 +01:00
sBubshait
4879775d0b Update donate priority to add an assertion that intrrupts are disabled, w/ T 2024-10-23 16:32:40 +01:00
sBubshait
6223846fde Update lock_acquire to disable interrupts to eliminate race-conditions, w/ T 2024-10-23 16:30:38 +01:00
Themis Demetriades
f9d82c92de Update thread_recalculate_priority to disable interrupts preventing race conditions for access to donors and priorities w/ S 2024-10-23 16:16:25 +01:00
Demetriades, Themis
25ca7b6522 Merge branch 'task1/themis/priority-donation' into 'task1/priority-donation'
Merge task1/themis/priority-donation into task1/priority-donation

See merge request lab2425_autumn/pintos_22!13
2024-10-23 15:03:07 +00:00
Themis Demetriades
5f8dea21be Fix donate_priority to disable interrupts for entire update of possibly-ready donatee's priority 2024-10-23 14:10:48 +01:00
Themis Demetriades
b0074c80f0 Refactor ready_list_reinsert to require being called with interrupts disabled 2024-10-23 13:51:07 +01:00
Themis Demetriades
2cd4da17a4 Refactor donate_priority to only allow for the current thread to donate its priority 2024-10-23 13:42:36 +01:00
Themis Demetriades
a875d5fcb4 Update donate_priority to only attempt to sort position of the donee that isn't waiting for a lock 2024-10-23 13:37:43 +01:00
sBubshait
d82176a2e2 Refactor lock release to follow PintOS indent style and use list functoins, w/ T 2024-10-22 22:52:29 +01:00
Themis Demetriades
7aec2e6862 Refactor donate_priority to include comments explaining its purpose and logic w/ S 2024-10-22 21:15:30 +01:00
sBubshait
78c6fd36e3 Refactor sema_up to add comments for clarity, w/ T 2024-10-22 21:04:14 +01:00
Themis Demetriades
48104b3a41 Refactor cond_signal to add comment clarifying the logic w/ S 2024-10-22 20:57:18 +01:00
sBubshait
5549b9c0cb Refactor thread recalculate priority to add comments for clarity, w/ T 2024-10-22 20:52:52 +01:00
sBubshait
bf6104200c Refactor thread set priority to remove unused variable and add comment, w/ T 2024-10-22 20:47:34 +01:00
Themis Demetriades
6983ccdd3b Update thread_get_priority description comment to be more specific w/ S 2024-10-22 20:41:44 +01:00
Themis Demetriades
fc1691f994 Refactor thread.h to remove superfluous thread priority comparison function w/ S 2024-10-22 20:36:37 +01:00
sBubshait
244db41434 Update condvar to use linear search due to changes in priority from donations, w/ T 2024-10-22 20:25:13 +01:00
Themis Demetriades
21cbfc9fe0 Implement transfer of orphaned donors when releasing lock w/ S 2024-10-22 20:06:21 +01:00
sBubshait
d9b9572631 Fix Bug in recalculating priority, uses 'elem' instead of 'donor_elem', w/ T 2024-10-22 19:49:53 +01:00
Themis Demetriades
a19d40bcca Reformat priority_more to obey code style 2024-10-22 19:01:55 +01:00
Themis Demetriades
f10514f4cc Update priority_more comment description to specify which list_elem member it is compatible with 2024-10-22 18:59:19 +01:00
Themis Demetriades
dae5b0d097 Update semaphore priority scheduling to use linear search on unordered list w/ S 2024-10-20 22:50:31 +01:00
Themis Demetriades
840df8af78 Fix bug in donate_priority that wouldn't update the list of donors w/ S 2024-10-20 22:29:20 +01:00
sBubshait
7f7b1648cd Fix indentation in recalculate priority to match pintos style, w/ T 2024-10-20 22:28:29 +01:00
sBubshait
44de31c0ff Fix Bug in Implementation of recalculate priority setting base priority, w/ T 2024-10-20 20:54:48 +01:00
Themis Demetriades
afcb12cef0 Reformat thread priority changing functions to follow style w/ S 2024-10-20 20:38:28 +01:00
sBubshait
b1dba1a0bd Add implementation for recalculate effective priority, w/ T 2024-10-20 20:37:03 +01:00
Themis Demetriades
8e20884a23 Update releasing of locks to update donation information w/ S 2024-10-20 20:17:27 +01:00
Themis Demetriades
ee0cf632b9 Fix attempt to donate priority within lock acquisition w/ S 2024-10-20 19:45:05 +01:00
sBubshait
343ac55d37 Implement priority donation helper function with propagation, w/ T 2024-10-20 19:42:35 +01:00
Themis Demetriades
f98e4bc81c Update lock_acquire to attempt donation of priorities, w/ S 2024-10-20 19:20:26 +01:00
sBubshait
5a651f1279 Update implementation of thread set priority to account for donations, w/ T 2024-10-20 18:06:07 +01:00
Themis Demetriades
fcc8cbb71e Implement initialization of new threads to track donation information, w/ S 2024-10-20 17:11:44 +01:00
sBubshait
8b1e0b9559 Add donation-related information to the thread structure, w/ T 2024-10-20 17:04:14 +01:00
cbb9d52455 Recover removed comment 2024-10-17 20:09:43 +01:00
Themis
6b9b671368 Update sema_up to properly yield during execution of an interrupt handler 2024-10-17 20:01:55 +01:00
sBubshait
bfdedc53e2 Update set priority so not to yield if no ready threads 2024-10-17 19:46:10 +01:00
Themis
711efdc78a Implement priority scheduling for condition variables 2024-10-17 19:43:35 +01:00
sBubshait
c9a9d57019 Merge branch 'task1/priority-scheduling' into task1/merged/priority-scheduling
# Conflicts:
#	.gitignore
#	src/threads/synch.c
#	src/threads/thread.c
2024-10-17 19:30:19 +01:00
044c383a0f fix mlfqs tests, complete BSD scheduling 2024-10-17 18:31:09 +01:00
c5e41db9b0 Implement BSD calculations
- load_avg, recent_cpu, priority calculations
- reduce frac bits to 14
- ignore thread_set_priority when BSD enabled
2024-10-17 18:31:09 +01:00
a7a6d0b9d4 Fix fixed-point returns 2024-10-17 18:31:09 +01:00
EDiasAlberto
d5f913de2b implement recent_cpu 2024-10-17 18:31:09 +01:00
630fbaa3ab implement thread_get_nice & thread_get_recent_cpu 2024-10-17 18:31:09 +01:00
82cc108c38 inline funcs instead of macros for fixed-point 2024-10-17 18:31:09 +01:00
EDiasAlberto
337f8828d9 implement thread_get_nice, thread_set_nice and skeleton for calculate_priority 2024-10-17 18:31:09 +01:00
EDiasAlberto
4e4b5fb83c modify gitignore to ignore vscode files 2024-10-17 18:31:09 +01:00
EDiasAlberto
112432dde0 define basic fixed-point macros 2024-10-17 18:31:09 +01:00
EDiasAlberto
f969b02630 define fixed point arithmetic constants
gitignore clion files
2024-10-17 18:31:09 +01:00
62cca87322 Merge branch 'gitlab-ci' into 'master'
GitLab CI & testing image

See merge request lab2425_autumn/pintos_22!2
2024-10-17 17:30:41 +00:00
sBubshait
88967acdaa Update cond_wait to insert into waiters list in sorted order of priority 2024-10-17 08:53:53 +01:00
sBubshait
e74ee59e17 Update sema priority compare to take aux optionally as priority of first sema 2024-10-17 08:46:14 +01:00
sBubshait
53b296d6c4 Add function to compare the priority of two semaphores 2024-10-17 08:36:29 +01:00
sBubshait
0d6fb2f167 Update sema_up to yield CPU if unblocked has higher priority 2024-10-17 07:15:10 +01:00
sBubshait
24900545d4 Update sema_down to insert into waiters list in priority sorted order 2024-10-17 06:54:10 +01:00
sBubshait
fb268cdef0 Update thread make priority_more public 2024-10-17 06:47:58 +01:00
sBubshait
dbb7fd56e3 Update set priority to reorder the list if priority is changed 2024-10-17 06:38:27 +01:00
sBubshait
949455aae5 Update set priority to eliminate race condition when accessing ready list 2024-10-17 06:35:39 +01:00
sBubshait
5c09ff0149 Updated set priority to yield if priority is lowered 2024-10-17 06:27:59 +01:00
sBubshait
9f53040d27 Update thread_set_priority to not do anything if priority is unchanged 2024-10-17 06:00:34 +01:00
sBubshait
62c8818c05 Update thread_yield to add thread back in sorted order of priority 2024-10-16 08:18:51 +01:00
sBubshait
fda79173c0 Update thread_unblock to maintain priority order in ready_list 2024-10-16 08:14:00 +01:00
sBubshait
1c53790ca7 Update set_priority to ensure that the new priority is within bounds 2024-10-16 08:13:56 +01:00
sBubshait
9bb0b758c8 Fix Error missing semicolon after function signature 2024-10-16 07:40:41 +01:00
sBubshait
8b3f9e353f Update creating thread to yield if the new thread has higher priority 2024-10-16 07:33:24 +01:00
sBubshait
83910f945c Add priority comparator function for thread list sorting 2024-10-16 07:26:02 +01:00
sBubshait
9f71c989a9 Update gitignore to ignore temporary Mac OS files and code editor folders 2024-10-16 07:10:31 +01:00
d51648d7dc GitLab CI & testing image 2024-10-10 17:34:33 +01:00
15 changed files with 1381 additions and 91 deletions

11
.gitignore vendored
View File

@@ -4,8 +4,12 @@
#ignore pdf files (just keep source files) #ignore pdf files (just keep source files)
*.pdf *.pdf
#ignore shell scripts (only used for testing) #ignore Mac OS generated files
*.sh .DS_Store
#ignore code editor generated directories
.idea
.vscode
#ignore junk files from latex output #ignore junk files from latex output
*.out *.out
@@ -27,3 +31,6 @@
*.nav *.nav
*.toc *.toc
#ignore files from CLion/VSCode IDEs
.idea
.vscode

44
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,44 @@
stages:
- test
.pintos_tests:
stage: test
image: gitlab.doc.ic.ac.uk:4567/lab2425_autumn/pintos_22/pintos-testing:latest
artifacts:
when: always
paths:
- src/$DIR/build/tests/$DIR/
before_script:
- cd src/utils
- make
- export PATH=$PWD:$PATH
- cd ../..
script:
- cd src/$DIR
- make check | tee build.log
- grep -q "FAIL tests/$DIR" build.log && exit 1 || exit 0
test_devices:
extends: .pintos_tests
variables:
DIR: devices
test_filesys:
extends: .pintos_tests
variables:
DIR: filesys
test_threads:
extends: .pintos_tests
variables:
DIR: threads
test_userprog:
extends: .pintos_tests
variables:
DIR: userprog
test_vm:
extends: .pintos_tests
variables:
DIR: vm

3
Dockerfile.devel Normal file
View File

@@ -0,0 +1,3 @@
FROM debian:12-slim
RUN apt update && apt install gcc perl make qemu-system-i386 gdb -y

View File

@@ -119,9 +119,6 @@ timer_sleep (int64_t ticks)
NULL); NULL);
intr_set_level (old_level); intr_set_level (old_level);
sema_down (&st.semaphore); sema_down (&st.semaphore);
old_level = intr_disable ();
list_remove (&st.elem);
intr_set_level (old_level);
} }
/* Sleeps for approximately MS milliseconds. Interrupts must be /* Sleeps for approximately MS milliseconds. Interrupts must be
@@ -204,7 +201,10 @@ timer_interrupt (struct intr_frame *args UNUSED)
{ {
struct asleep_thread *st = list_entry (e, struct asleep_thread, elem); struct asleep_thread *st = list_entry (e, struct asleep_thread, elem);
if (ticks >= st->end_at) if (ticks >= st->end_at)
sema_up (&st->semaphore); {
list_remove (&st->elem);
sema_up (&st->semaphore);
}
else else
break; break;
} }

View File

@@ -4,6 +4,9 @@
#include "filesys/off_t.h" #include "filesys/off_t.h"
#include <stdbool.h> #include <stdbool.h>
/* The maximum length of a file name in PintOS. */
#define FNAME_MAX_LEN 14
struct inode; struct inode;
/* Opening and closing files. */ /* Opening and closing files. */

View File

@@ -170,6 +170,9 @@ list_insert (struct list_elem *before, struct list_elem *elem)
{ {
ASSERT (is_interior (before) || is_tail (before)); ASSERT (is_interior (before) || is_tail (before));
ASSERT (elem != NULL); ASSERT (elem != NULL);
// Sanity checks to prevent (some) loop lists
ASSERT (before != elem);
ASSERT (before->prev != elem);
elem->prev = before->prev; elem->prev = before->prev;
elem->next = before; elem->next = before;

View File

@@ -166,7 +166,7 @@ mkdir (const char *dir)
} }
bool bool
readdir (int fd, char name[READDIR_MAX_LEN + 1]) readdir (int fd, char name[FNAME_MAX_LEN + 1])
{ {
return syscall2 (SYS_READDIR, fd, name); return syscall2 (SYS_READDIR, fd, name);
} }

View File

@@ -3,6 +3,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <debug.h> #include <debug.h>
#include "../../filesys/file.h"
/* Process identifier. */ /* Process identifier. */
typedef int pid_t; typedef int pid_t;
@@ -12,9 +13,6 @@ typedef int pid_t;
typedef int mapid_t; typedef int mapid_t;
#define MAP_FAILED ((mapid_t) -1) #define MAP_FAILED ((mapid_t) -1)
/* Maximum characters in a filename written by readdir(). */
#define READDIR_MAX_LEN 14
/* Typical return values from main() and arguments to exit(). */ /* Typical return values from main() and arguments to exit(). */
#define EXIT_SUCCESS 0 /* Successful execution. */ #define EXIT_SUCCESS 0 /* Successful execution. */
#define EXIT_FAILURE 1 /* Unsuccessful execution. */ #define EXIT_FAILURE 1 /* Unsuccessful execution. */
@@ -41,7 +39,7 @@ void munmap (mapid_t);
/* Task 4 only. */ /* Task 4 only. */
bool chdir (const char *dir); bool chdir (const char *dir);
bool mkdir (const char *dir); bool mkdir (const char *dir);
bool readdir (int fd, char name[READDIR_MAX_LEN + 1]); bool readdir (int fd, char name[FNAME_MAX_LEN + 1]);
bool isdir (int fd); bool isdir (int fd);
int inumber (int fd); int inumber (int fd);

100
src/threads/fixed-point.h Normal file
View File

@@ -0,0 +1,100 @@
#include <stdint.h>
#ifndef FIXED_POINT_H
#define FIXED_POINT_H
typedef struct
{
int32_t raw;
} fp32_t;
/* Fixed Point Arithmetic bit count constants */
#define NUM_FRAC_BITS 14
#define NUM_INT_BITS (31 - NUM_FRAC_BITS)
#define CONVERSION_FACTOR (1 << NUM_FRAC_BITS) /* f = 2^q, (2^14) */
/* Fixed Point Arithmetic conversion operations */
/* Converts an integer n to a fixed point number */
inline fp32_t
fp_from_int (int32_t n)
{
return (fp32_t){ n * CONVERSION_FACTOR };
}
/* Handles conversion of fixed point to integer,
with truncation */
inline int32_t
fp_floor (fp32_t x)
{
return x.raw / CONVERSION_FACTOR;
}
/* Handles conversion of fixed point to integer,
with rounding */
inline int32_t
fp_round (fp32_t x)
{
if (x.raw >= 0)
return (x.raw + CONVERSION_FACTOR / 2) / CONVERSION_FACTOR;
else
return (x.raw - CONVERSION_FACTOR / 2) / CONVERSION_FACTOR;
}
/* Add two fixed points */
inline fp32_t
fp_add (fp32_t x, fp32_t y)
{
return (fp32_t){ x.raw + y.raw };
}
/* Subtract two fixed points */
inline fp32_t
fp_sub (fp32_t x, fp32_t y)
{
return (fp32_t){ x.raw - y.raw };
}
/* Multiple two fixed points */
inline fp32_t
fp_mul (fp32_t x, fp32_t y)
{
return (fp32_t){ ((int64_t)x.raw) * y.raw / CONVERSION_FACTOR };
}
/* Divide two fixed points */
inline fp32_t
fp_div (fp32_t x, fp32_t y)
{
return (fp32_t){ ((int64_t)x.raw) * CONVERSION_FACTOR / y.raw };
}
/* Multiply fixed point and integer */
inline fp32_t
fp_mul_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw * n };
}
/* Divide fixed point by integer */
inline fp32_t
fp_div_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw / n };
}
/* Add fixed point to integer */
inline fp32_t
fp_add_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw + n * CONVERSION_FACTOR };
}
/* Subtract integer from fixed point */
inline fp32_t
fp_sub_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw - n * CONVERSION_FACTOR };
}
#endif //FIXED_POINT_H

View File

@@ -32,6 +32,10 @@
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/thread.h" #include "threads/thread.h"
static bool
priority_less (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED);
/* Initializes semaphore SEMA to VALUE. A semaphore is a /* Initializes semaphore SEMA to VALUE. A semaphore is a
nonnegative integer along with two atomic operators for nonnegative integer along with two atomic operators for
manipulating it: manipulating it:
@@ -68,8 +72,7 @@ sema_down (struct semaphore *sema)
old_level = intr_disable (); old_level = intr_disable ();
while (sema->value == 0) while (sema->value == 0)
{ {
list_insert_ordered (&sema->waiters, &thread_current ()->elem, list_push_back (&sema->waiters, &thread_current ()->elem);
&thread_priority_greater, NULL);
thread_block (); thread_block ();
} }
sema->value--; sema->value--;
@@ -107,19 +110,36 @@ sema_try_down (struct semaphore *sema)
This function may be called from an interrupt handler. */ This function may be called from an interrupt handler. */
void void
sema_up (struct semaphore *sema) sema_up (struct semaphore *sema)
{ {
enum intr_level old_level; enum intr_level old_level;
bool thread_unblocked = false; /* Flag to track if any thread was woken up. */
ASSERT (sema != NULL); ASSERT (sema != NULL);
old_level = intr_disable (); old_level = intr_disable ();
if (!list_empty (&sema->waiters)) if (!list_empty (&sema->waiters))
thread_unblock (list_entry (list_pop_front (&sema->waiters), {
struct thread, elem)); /* Enforces wake-up of the highest priority thread waiting for the
semaphore. */
struct list_elem *e = list_max (&sema->waiters, priority_less, NULL);
list_remove (e);
thread_unblock (list_entry (e, struct thread, elem));
thread_unblocked = true;
}
sema->value++; sema->value++;
intr_set_level (old_level); intr_set_level (old_level);
thread_yield ();
/* Yields the CPU in case the thread that has been woken up has a higher
priority that the current running thread, including the case when called
within an interrupt handler. */
if (thread_unblocked)
{
if (intr_context ())
intr_yield_on_return ();
else
thread_yield ();
}
} }
static void sema_test_helper (void *sema_); static void sema_test_helper (void *sema_);
@@ -183,6 +203,48 @@ lock_init (struct lock *lock)
sema_init (&lock->semaphore, 1); sema_init (&lock->semaphore, 1);
} }
/* Current thread donates its priority to donee, iteratively
propagating the donation in the case of chains in the wait-for graph.
Also keeps track of the donation by updating the donors list. Expects
interrupts to be disabled. */
static void
donate_priority (struct thread *donee) {
ASSERT (intr_get_level () == INTR_OFF);
struct thread *donor = thread_current ();
list_remove (&donor->donor_elem);
list_push_back (&donee->donors_list, &donor->donor_elem);
while (donee != NULL)
{
/* Stop propagation of donation once a donee is reached that has
a higher effective priority (as its donees can't have less
priority than that being donated). */
if (donor->priority <= donee->priority)
break;
/* Also stop propagation of donation once a donee is reached with
no donees of its own (sink node in WFG). */
if (donee->waiting_lock == NULL)
{
/* Only the sink node of the WFG isn't waiting for a lock and
could be on the ready list. Thus, as its priority changed,
it must be reinserted into the list. */
enum intr_level old_level = intr_disable ();
donee->priority = donor->priority;
ready_list_reinsert (donee);
intr_set_level (old_level);
donee = NULL;
}
else
{
donee->priority = donor->priority;
donee = donee->waiting_lock->holder;
}
}
}
/* Acquires LOCK, sleeping until it becomes available if /* Acquires LOCK, sleeping until it becomes available if
necessary. The lock must not already be held by the current necessary. The lock must not already be held by the current
thread. thread.
@@ -198,8 +260,20 @@ lock_acquire (struct lock *lock)
ASSERT (!intr_context ()); ASSERT (!intr_context ());
ASSERT (!lock_held_by_current_thread (lock)); ASSERT (!lock_held_by_current_thread (lock));
struct thread *t = thread_current ();
ASSERT (t->waiting_lock == NULL);
enum intr_level old_level = intr_disable ();
if (lock->holder != NULL)
{
t->waiting_lock = lock;
donate_priority (lock->holder);
}
intr_set_level (old_level);
sema_down (&lock->semaphore); sema_down (&lock->semaphore);
lock->holder = thread_current (); lock->holder = thread_current ();
t->waiting_lock = NULL;
} }
/* Tries to acquires LOCK and returns true if successful or false /* Tries to acquires LOCK and returns true if successful or false
@@ -233,6 +307,51 @@ lock_release (struct lock *lock)
ASSERT (lock != NULL); ASSERT (lock != NULL);
ASSERT (lock_held_by_current_thread (lock)); ASSERT (lock_held_by_current_thread (lock));
struct thread *current_thread = thread_current ();
struct thread *max_donor = NULL;
struct list orphan_list;
list_init (&orphan_list);
enum intr_level old_level = intr_disable ();
/* Loop through current thread's donors, removing the ones waiting for the
lock being released and keeping track of them (within orphan_list).
Also identifies the highest priority donor thread among them. */
struct list_elem *tail = list_tail (&current_thread->donors_list);
struct list_elem *e = list_begin (&current_thread->donors_list);
while (e != tail)
{
struct thread *donor = list_entry (e, struct thread, donor_elem);
struct list_elem *next = list_next (e);
/* Excludes donors that aren't waiting for the lock being released,
and tracks the rest. */
if (donor->waiting_lock == lock)
{
list_remove (e);
list_push_back (&orphan_list, e);
/* Identify highest priority donor. */
if (max_donor == NULL || donor->priority > max_donor->priority)
max_donor = donor;
}
e = next;
}
/* If there exists a maximum donor thread waiting for this lock to be
released, transfer the remaining orphaned donors to its donor list. */
if (max_donor != NULL)
{
while (!list_empty (&orphan_list))
list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list));
}
intr_set_level (old_level);
/* Removal of donors to this thread may change its effective priority,
so recalculate. */
thread_recalculate_priority ();
lock->holder = NULL; lock->holder = NULL;
sema_up (&lock->semaphore); sema_up (&lock->semaphore);
} }
@@ -255,6 +374,49 @@ struct semaphore_elem
struct semaphore semaphore; /* This semaphore. */ struct semaphore semaphore; /* This semaphore. */
}; };
/* Function that compares the two threads associated with the provided
pointers to their 'elem' member. Returns true if the thread associated
with a_ has a lower priority than that of b_. */
static bool
priority_less (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED)
{
struct thread *a = list_entry (a_, struct thread, elem);
struct thread *b = list_entry (b_, struct thread, elem);
return a->priority < b->priority;
}
/* Function that compares the two *semaphores* associated with the provided
list_elem structures. [i.e., takes list_elem of semaphore_elem, and]
Returns true if the thread associated with the semaphore associated with a_
has a higher priority than that of b_.
If aux is provided, then it is a pointer to an integer representing the
priority of the first semaphore. This is useful when the thread has not been
sema'd down yet. */
static bool
sema_priority_more(const struct list_elem *a, const struct list_elem *b,
void *inserting_telem)
{
struct list_elem *te_a, *te_b;
te_b = list_front (
&list_entry (b, struct semaphore_elem, elem)->semaphore.waiters);
if (inserting_telem == NULL)
{
te_a = list_front (
&list_entry (a, struct semaphore_elem, elem)->semaphore.waiters);
}
else
{
te_a = inserting_telem;
}
return priority_more (te_a, te_b, NULL);
}
/* Initializes condition variable COND. A condition variable /* Initializes condition variable COND. A condition variable
allows one piece of code to signal a condition and cooperating allows one piece of code to signal a condition and cooperating
code to receive the signal and act upon it. */ code to receive the signal and act upon it. */
@@ -318,9 +480,14 @@ cond_signal (struct condition *cond, struct lock *lock UNUSED)
ASSERT (!intr_context ()); ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock)); ASSERT (lock_held_by_current_thread (lock));
if (!list_empty (&cond->waiters)) if (!list_empty (&cond->waiters))
sema_up (&list_entry (list_pop_front (&cond->waiters), {
struct semaphore_elem, elem)->semaphore); /* Enforce wake-up of highest priority thread within the singleton
semaphores waiting for condvar. */
struct list_elem *e = list_min (&cond->waiters, sema_priority_more, NULL);
list_remove (e);
sema_up (&list_entry (e, struct semaphore_elem, elem)->semaphore);
}
} }
/* Wakes up all threads, if any, waiting on COND (protected by /* Wakes up all threads, if any, waiting on COND (protected by

View File

@@ -1,18 +1,23 @@
#include "threads/thread.h" #include "threads/thread.h"
#include <debug.h> #include <debug.h>
#include <hash.h>
#include <stddef.h> #include <stddef.h>
#include <random.h> #include <random.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "devices/timer.h"
#include "threads/fixed-point.h"
#include "threads/flags.h" #include "threads/flags.h"
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/intr-stubs.h" #include "threads/intr-stubs.h"
#include "threads/malloc.h"
#include "threads/palloc.h" #include "threads/palloc.h"
#include "threads/switch.h" #include "threads/switch.h"
#include "threads/synch.h" #include "threads/synch.h"
#include "threads/vaddr.h" #include "threads/vaddr.h"
#ifdef USERPROG #ifdef USERPROG
#include "userprog/process.h" #include "userprog/process.h"
#include "userprog/syscall.h"
#endif #endif
/* Random value for struct thread's `magic' member. /* Random value for struct thread's `magic' member.
@@ -49,9 +54,11 @@ struct kernel_thread_frame
static long long idle_ticks; /* # of timer ticks spent idle. */ static long long idle_ticks; /* # of timer ticks spent idle. */
static long long kernel_ticks; /* # of timer ticks in kernel threads. */ static long long kernel_ticks; /* # of timer ticks in kernel threads. */
static long long user_ticks; /* # of timer ticks in user programs. */ static long long user_ticks; /* # of timer ticks in user programs. */
static fp32_t load_avg = { 0 }; /* System load average. */
/* Scheduling. */ /* Scheduling. */
#define TIME_SLICE 4 /* # of timer ticks to give each thread. */ #define TIME_SLICE 4 /* # of timer ticks to give each thread. */
#define PRI_UPDATE_FREQ 4 /* # of timer ticks to update priorities. */
static unsigned thread_ticks; /* # of timer ticks since last yield. */ static unsigned thread_ticks; /* # of timer ticks since last yield. */
/* If false (default), use round-robin scheduler. /* If false (default), use round-robin scheduler.
@@ -64,12 +71,19 @@ static void kernel_thread (thread_func *, void *aux);
static void idle (void *aux UNUSED); static void idle (void *aux UNUSED);
static struct thread *running_thread (void); static struct thread *running_thread (void);
static struct thread *next_thread_to_run (void); static struct thread *next_thread_to_run (void);
static void init_thread (struct thread *, const char *name, int priority); static void init_process_result (struct thread *t);
static void init_thread (struct thread *, const char *name, int nice,
int priority, fp32_t recent_cpu);
static bool is_thread (struct thread *) UNUSED; static bool is_thread (struct thread *) UNUSED;
static void *alloc_frame (struct thread *, size_t size); static void *alloc_frame (struct thread *, size_t size);
static int calculate_bsd_priority (fp32_t recent_cpu, int nice);
static void update_recent_cpu (struct thread *t, void *aux UNUSED);
static void recalculate_priority (struct thread *t);
static void schedule (void); static void schedule (void);
void thread_schedule_tail (struct thread *prev); void thread_schedule_tail (struct thread *prev);
static tid_t allocate_tid (void); static tid_t allocate_tid (void);
static bool donor_priority_less (const struct list_elem *a_,
const struct list_elem *b_, void *aux UNUSED);
/* Initializes the threading system by transforming the code /* Initializes the threading system by transforming the code
that's currently running into a thread. This can't work in that's currently running into a thread. This can't work in
@@ -95,9 +109,12 @@ thread_init (void)
/* Set up a thread structure for the running thread. */ /* Set up a thread structure for the running thread. */
initial_thread = running_thread (); initial_thread = running_thread ();
init_thread (initial_thread, "main", PRI_DEFAULT); fp32_t initial_thread_recent_cpu = { 0 };
init_thread (initial_thread, "main", NICE_DEFAULT, PRI_DEFAULT,
initial_thread_recent_cpu);
initial_thread->status = THREAD_RUNNING; initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid (); initial_thread->tid = allocate_tid ();
initial_thread->result = NULL; /* Main thread cannot be waited for. */
} }
/* Starts preemptive thread scheduling by enabling interrupts. /* Starts preemptive thread scheduling by enabling interrupts.
@@ -145,6 +162,35 @@ thread_tick (void)
else else
kernel_ticks++; kernel_ticks++;
/* Update system load_avg and all threads recent_cpu every second. */
int64_t ticks = timer_ticks ();
if (thread_mlfqs)
{
if (t != idle_thread)
t->recent_cpu = fp_add_int (t->recent_cpu, 1);
if (ticks % TIMER_FREQ == 0)
{
size_t ready = threads_ready ();
if (t != idle_thread)
ready++;
fp32_t old_coeff = fp_div_int (fp_mul_int (load_avg, 59), 60);
fp32_t new_coeff = fp_div_int (fp_from_int (ready), 60);
load_avg = fp_add (old_coeff, new_coeff);
thread_foreach (update_recent_cpu, NULL);
/* Priorities have been updated, need to re-sort. */
list_sort (&ready_list, priority_more, NULL);
}
/* Recent cpu was updated, update priority. */
if (t != idle_thread && ticks % PRI_UPDATE_FREQ == 0)
{
t->base_priority = calculate_bsd_priority (t->recent_cpu, t->nice);
recalculate_priority (t);
}
}
/* Enforce preemption. */ /* Enforce preemption. */
if (++thread_ticks >= TIME_SLICE) if (++thread_ticks >= TIME_SLICE)
intr_yield_on_return (); intr_yield_on_return ();
@@ -192,8 +238,14 @@ thread_create (const char *name, int priority,
return TID_ERROR; return TID_ERROR;
/* Initialize thread. */ /* Initialize thread. */
init_thread (t, name, priority); struct thread *parent_thread = thread_current ();
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu);
tid = t->tid = allocate_tid (); tid = t->tid = allocate_tid ();
init_process_result (t);
#ifdef USERPROG
hash_init (&t->open_files, fd_hash, fd_less, NULL);
#endif
/* Prepare thread for first run by initializing its stack. /* Prepare thread for first run by initializing its stack.
Do this atomically so intermediate values for the 'stack' Do this atomically so intermediate values for the 'stack'
@@ -217,9 +269,17 @@ thread_create (const char *name, int priority,
intr_set_level (old_level); intr_set_level (old_level);
/* No need to synchronise child_results since it is only ever accessed by one
thread. By the nature of increasing TIDs, this list is ordered. */
list_push_back (&parent_thread->child_results, &t->result->elem);
/* Add to run queue. */ /* Add to run queue. */
thread_unblock (t); thread_unblock (t);
thread_yield ();
/* Yield if the newly created thread has higher priority than the current
thread. */
if (t->priority > thread_get_priority ())
thread_yield ();
return tid; return tid;
} }
@@ -257,7 +317,10 @@ thread_unblock (struct thread *t)
old_level = intr_disable (); old_level = intr_disable ();
ASSERT (t->status == THREAD_BLOCKED); ASSERT (t->status == THREAD_BLOCKED);
list_insert_ordered (&ready_list, &t->elem, &thread_priority_greater, NULL);
/* Insert the thread back into the ready list in priority order. */
list_insert_ordered (&ready_list, &t->elem, priority_more, NULL);
t->status = THREAD_READY; t->status = THREAD_READY;
intr_set_level (old_level); intr_set_level (old_level);
} }
@@ -310,7 +373,9 @@ thread_exit (void)
and schedule another process. That process will destroy us and schedule another process. That process will destroy us
when it calls thread_schedule_tail(). */ when it calls thread_schedule_tail(). */
intr_disable (); intr_disable ();
list_remove (&thread_current()->allelem); struct thread *t = thread_current ();
list_remove (&t->allelem);
list_remove (&t->donor_elem);
thread_current ()->status = THREAD_DYING; thread_current ()->status = THREAD_DYING;
schedule (); schedule ();
NOT_REACHED (); NOT_REACHED ();
@@ -327,9 +392,13 @@ thread_yield (void)
ASSERT (!intr_context ()); ASSERT (!intr_context ());
old_level = intr_disable (); old_level = intr_disable ();
if (cur != idle_thread)
list_insert_ordered (&ready_list, &cur->elem, thread_priority_greater, if (cur != idle_thread)
NULL); {
/* Insert the thread back into the ready list in priority order. */
list_insert_ordered (&ready_list, &cur->elem, priority_more, NULL);
}
cur->status = THREAD_READY; cur->status = THREAD_READY;
schedule (); schedule ();
intr_set_level (old_level); intr_set_level (old_level);
@@ -352,54 +421,161 @@ thread_foreach (thread_action_func *func, void *aux)
} }
} }
/* Sets the current thread's priority to NEW_PRIORITY. */ /* Function that compares the two threads associated with the provided
void pointers to their 'elem' member. Returns true if the thread associated
thread_set_priority (int new_priority) with a_ has a higher priority than that of b_. */
bool
priority_more (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED)
{ {
ASSERT (PRI_MIN <= new_priority && new_priority <= PRI_MAX); struct thread *a = list_entry (a_, struct thread, elem);
int old_priority = thread_get_priority (); struct thread *b = list_entry (b_, struct thread, elem);
return a->priority > b->priority;
thread_current ()->priority = new_priority;
if (new_priority < old_priority)
thread_yield ();
} }
/* Returns the current thread's priority. */ /* Function that compares the two threads associated with the provided
pointers to their 'donor_elem' member. Returns true if the thread associated
with a_ has a lower priority than that of b_. */
static bool
donor_priority_less (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED)
{
struct thread *a = list_entry (a_, struct thread, donor_elem);
struct thread *b = list_entry (b_, struct thread, donor_elem);
return a->priority < b->priority;
}
/* Sets the current thread's base priority to new_base_priority.
Updates the current thread's effective priority if necessary. */
void
thread_set_priority (int new_base_priority)
{
if (thread_mlfqs)
return;
ASSERT (new_base_priority >= PRI_MIN);
ASSERT (new_base_priority <= PRI_MAX);
struct thread *t = thread_current ();
/* If the base priority is unchanged, do nothing. */
if (new_base_priority == t->base_priority)
return;
t->base_priority = new_base_priority;
recalculate_priority (t);
thread_yield ();
}
/* Returns the current thread's effective priority. */
int int
thread_get_priority (void) thread_get_priority (void)
{ {
return thread_current ()->priority; return thread_current ()->priority;
} }
/* Updates recent_cpu for a thread. */
static void
update_recent_cpu (struct thread *t, void *aux UNUSED)
{
fp32_t curr_recent_cpu = t->recent_cpu;
fp32_t dbl_load_avg = fp_mul_int (load_avg, 2);
fp32_t recent_cpu_coeff
= fp_div (dbl_load_avg, fp_add_int (dbl_load_avg, 1));
t->recent_cpu
= fp_add_int (fp_mul (recent_cpu_coeff, curr_recent_cpu), t->nice);
// recent_cpu was updated, update priority.
t->base_priority = calculate_bsd_priority (t->recent_cpu, t->nice);
recalculate_priority (t);
}
/* Recalculates the effective priority of the current thread. */
void
thread_recalculate_priority (void)
{
struct thread *t = thread_current ();
recalculate_priority (t);
}
static void
recalculate_priority (struct thread *t)
{
enum intr_level old_level = intr_disable ();
t->priority = t->base_priority;
/* If there are no donors to the current thread, then the effective
priority is just the base priority. */
if (!list_empty (&t->donors_list))
{
int max_donated_priority =
list_entry (list_max (&t->donors_list, donor_priority_less, NULL),
struct thread, donor_elem)->priority;
/* The effective priority is the max donated priority if this is
higher than the base priority. */
if (max_donated_priority > t->priority)
t->priority = max_donated_priority;
}
intr_set_level (old_level);
}
/* Sets the current thread's nice value to NICE. */ /* Sets the current thread's nice value to NICE. */
void void
thread_set_nice (int nice UNUSED) thread_set_nice (int nice)
{ {
/* Not yet implemented. */ ASSERT (NICE_MIN <= nice && nice <= NICE_MAX);
struct thread *t = thread_current ();
t->nice = nice;
t->base_priority = calculate_bsd_priority (t->recent_cpu, t->nice);
recalculate_priority (t);
struct thread *next_t
= list_entry (list_begin (&ready_list), struct thread, elem);
if (t->priority < next_t->priority)
thread_yield ();
} }
/* Returns the current thread's nice value. */ /* Returns the current thread's nice value. */
int int
thread_get_nice (void) thread_get_nice (void)
{ {
/* Not yet implemented. */ return thread_current ()->nice;
return 0;
} }
/* Returns 100 times the system load average. */ /* Returns 100 times the system load average. */
int int
thread_get_load_avg (void) thread_get_load_avg (void)
{ {
/* Not yet implemented. */ return fp_round (fp_mul_int (load_avg, 100));
return 0;
} }
/* Returns 100 times the current thread's recent_cpu value. */ /* Returns 100 times the current thread's recent_cpu value. */
int int
thread_get_recent_cpu (void) thread_get_recent_cpu (void)
{ {
/* Not yet implemented. */ return fp_round (fp_mul_int (thread_current ()->recent_cpu, 100));
return 0; }
/* Reinsert thread t into the ready list at its correct position
in descending order of priority. Used when this thread's priority
may have changed. Must be called with interrupts disabled. */
void
ready_list_reinsert (struct thread *t)
{
ASSERT (intr_get_level () == INTR_OFF);
/* If the thread isn't ready to run, do nothing. */
if (t->status != THREAD_READY)
return;
list_remove (&t->elem);
list_insert_ordered (&ready_list, &t->elem, priority_more, NULL);
} }
/* Idle thread. Executes when no other thread is ready to run. /* Idle thread. Executes when no other thread is ready to run.
@@ -472,10 +648,23 @@ is_thread (struct thread *t)
return t != NULL && t->magic == THREAD_MAGIC; return t != NULL && t->magic == THREAD_MAGIC;
} }
/* Allocate and initialise a process result for given thread. */
static void
init_process_result (struct thread *t)
{
struct process_result *result = malloc (sizeof (struct process_result));
result->tid = t->tid;
result->exit_status = t->exit_status;
lock_init (&result->lock);
sema_init (&result->sema, 0);
t->result = result;
}
/* Does basic initialization of T as a blocked thread named /* Does basic initialization of T as a blocked thread named
NAME. */ NAME. */
static void static void
init_thread (struct thread *t, const char *name, int priority) init_thread (struct thread *t, const char *name, int nice, int priority,
fp32_t recent_cpu)
{ {
enum intr_level old_level; enum intr_level old_level;
@@ -487,9 +676,21 @@ init_thread (struct thread *t, const char *name, int priority)
t->status = THREAD_BLOCKED; t->status = THREAD_BLOCKED;
strlcpy (t->name, name, sizeof t->name); strlcpy (t->name, name, sizeof t->name);
t->stack = (uint8_t *) t + PGSIZE; t->stack = (uint8_t *) t + PGSIZE;
t->priority = priority;
t->magic = THREAD_MAGIC; t->magic = THREAD_MAGIC;
t->base_priority
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
list_init (&t->donors_list);
list_push_back (&t->donors_list, &t->donor_elem);
t->waiting_lock = NULL;
t->nice = nice;
t->recent_cpu = recent_cpu;
t->priority = t->base_priority;
t->exit_status = -1;
list_init (&t->child_results);
old_level = intr_disable (); old_level = intr_disable ();
list_push_back (&all_list, &t->allelem); list_push_back (&all_list, &t->allelem);
intr_set_level (old_level); intr_set_level (old_level);
@@ -568,6 +769,21 @@ thread_schedule_tail (struct thread *prev)
} }
} }
/* Calculates BSD priority for a thread */
static int
calculate_bsd_priority (fp32_t recent_cpu, int nice)
{
ASSERT (thread_mlfqs);
int priority = PRI_MAX - (fp_round (recent_cpu) / 4) - (nice * 2);
if (priority < PRI_MIN)
return PRI_MIN;
if (priority > PRI_MAX)
return PRI_MAX;
return priority;
}
/* Schedules a new process. At entry, interrupts must be off and /* Schedules a new process. At entry, interrupts must be off and
the running process's state must have been changed from the running process's state must have been changed from
running to some other state. This function finds another running to some other state. This function finds another
@@ -605,17 +821,6 @@ allocate_tid (void)
return tid; return tid;
} }
/* Returns true iff the priority of the first list element's thread is greater
than that of the second list element's thread. */
bool
thread_priority_greater (const struct list_elem *a, const struct list_elem *b,
void *aux UNUSED)
{
struct thread *ta = list_entry (a, struct thread, elem);
struct thread *tb = list_entry (b, struct thread, elem);
return ta->priority > tb->priority;
}
/* Offset of `stack' member within `struct thread'. /* Offset of `stack' member within `struct thread'.
Used by switch.S, which can't figure it out on its own. */ Used by switch.S, which can't figure it out on its own. */
uint32_t thread_stack_ofs = offsetof (struct thread, stack); uint32_t thread_stack_ofs = offsetof (struct thread, stack);

View File

@@ -2,8 +2,12 @@
#define THREADS_THREAD_H #define THREADS_THREAD_H
#include <debug.h> #include <debug.h>
#include <hash.h>
#include <list.h> #include <list.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "threads/synch.h"
#include "threads/fixed-point.h"
/* States in a thread's life cycle. */ /* States in a thread's life cycle. */
enum thread_status enum thread_status
@@ -24,6 +28,22 @@ typedef int tid_t;
#define PRI_DEFAULT 31 /* Default priority. */ #define PRI_DEFAULT 31 /* Default priority. */
#define PRI_MAX 63 /* Highest priority. */ #define PRI_MAX 63 /* Highest priority. */
#define NICE_MIN -20 /* Lowest niceness. */
#define NICE_DEFAULT 0 /* Default niceness. */
#define NICE_MAX 20 /* Highest niceness. */
/* A process result, synchronised between parent and child. */
struct process_result
{
tid_t tid; /* The tid of the child process. */
int exit_status; /* The exit status of the child process. Initially set
to -1, then to exit_status when child dies. */
struct lock lock; /* Lock the exit_status and sema. */
struct semaphore sema; /* Semaphore to signal the parent that the exit_status
has been set. */
struct list_elem elem; /* List element for the parent's children list. */
};
/* A kernel thread or user process. /* A kernel thread or user process.
Each thread structure is stored in its own 4 kB page. The Each thread structure is stored in its own 4 kB page. The
@@ -90,12 +110,34 @@ struct thread
int priority; /* Priority. */ int priority; /* Priority. */
struct list_elem allelem; /* List element for all threads list. */ struct list_elem allelem; /* List element for all threads list. */
/* Donation Related */
int base_priority; /* Base priority of the thread. */
struct list donors_list; /* List of threads that have donated
to this thread. */
struct lock *waiting_lock; /* The lock that the current thread is
waiting for. */
struct list_elem donor_elem; /* List element so that thread can be
enlisted in other donors list. */
/* MLFQS items */
int nice; /* Nice value for this thread */
fp32_t recent_cpu; /* Amount of time this process received */
/* Process wait properties. */
struct process_result *result; /* Result of the process. */
struct list child_results; /* List of children's of this thread
process results. */
struct file *exec_file; /* Thread's currently running file */
/* Shared between thread.c and synch.c. */ /* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */ struct list_elem elem; /* List element. */
int exit_status; /* Exit Status: 0 = successful exit. */
#ifdef USERPROG #ifdef USERPROG
/* Owned by userprog/process.c. */ /* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */ uint32_t *pagedir; /* Page directory. */
struct hash open_files; /* Hash Table of FD -> Struct File */
#endif #endif
/* Owned by thread.c. */ /* Owned by thread.c. */
@@ -131,15 +173,17 @@ void thread_yield (void);
typedef void thread_action_func (struct thread *t, void *aux); typedef void thread_action_func (struct thread *t, void *aux);
void thread_foreach (thread_action_func *, void *); void thread_foreach (thread_action_func *, void *);
bool priority_more (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED);
int thread_get_priority (void); int thread_get_priority (void);
void thread_set_priority (int); void thread_set_priority (int);
void thread_recalculate_priority (void);
int thread_get_nice (void); int thread_get_nice (void);
void thread_set_nice (int); void thread_set_nice (int);
int thread_get_recent_cpu (void); int thread_get_recent_cpu (void);
int thread_get_load_avg (void); int thread_get_load_avg (void);
/* Returns true iff the priority of the first list element's thread is greater void ready_list_reinsert (struct thread *t);
than that of the second list element's thread. */
list_less_func thread_priority_greater;
#endif /* threads/thread.h */ #endif /* threads/thread.h */

View File

@@ -1,12 +1,14 @@
#include "userprog/process.h" #include "userprog/process.h"
#include <debug.h> #include <debug.h>
#include <inttypes.h> #include <inttypes.h>
#include <list.h>
#include <round.h> #include <round.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "userprog/gdt.h" #include "userprog/gdt.h"
#include "userprog/pagedir.h" #include "userprog/pagedir.h"
#include "userprog/syscall.h"
#include "userprog/tss.h" #include "userprog/tss.h"
#include "filesys/directory.h" #include "filesys/directory.h"
#include "filesys/file.h" #include "filesys/file.h"
@@ -14,57 +16,158 @@
#include "threads/flags.h" #include "threads/flags.h"
#include "threads/init.h" #include "threads/init.h"
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/synch.h"
#include "threads/palloc.h" #include "threads/palloc.h"
#include "threads/malloc.h"
#include "threads/thread.h" #include "threads/thread.h"
#include "threads/vaddr.h" #include "threads/vaddr.h"
#include "threads/synch.h"
#include "devices/timer.h"
/* Defines the native number of bytes processed by the processor
(for the purposes of alignment). */
#define WORD_SIZE 4
/* Keeps track of the position of pointers to user program arguments
within a linked list. */
struct arg_elem
{
char* arg;
struct list_elem elem;
};
/* Holds the data required to be passed from a kernel thread to a thread
that executes process_start for the purpose of starting a user process. */
struct process_start_data
{
char *cmd; /* Pointer to a copy of the command used to execute the process.
Allocated a page that must be freed by process_start. */
char *cmd_saveptr; /* Value pointed to by 'saveptr' argument used by
successive calls to strtok_r to split 'cmd' into
tokens while maintaining state. */
char file_name[FNAME_MAX_LEN + 1]; /* Name of the file of the process to
be started. */
struct semaphore load_sema;
bool success;
};
static thread_func start_process NO_RETURN; static thread_func start_process NO_RETURN;
static bool load (const char *cmdline, void (**eip) (void), void **esp); static bool load (const char *cmdline, void (**eip) (void), void **esp);
/* Starts a new thread running a user program loaded from /* Starts a new thread running a user program executed via
FILENAME. The new thread may be scheduled (and may even exit) CMD. The new thread may be scheduled (and may even exit)
before process_execute() returns. Returns the new process's before process_execute() returns. Returns the new process's
thread id, or TID_ERROR if the thread cannot be created. */ thread id, or TID_ERROR if the thread cannot be created. */
tid_t tid_t
process_execute (const char *file_name) process_execute (const char *cmd)
{ {
char *fn_copy; char *cmd_copy;
tid_t tid; tid_t tid;
struct process_start_data data;
/* Make a copy of FILE_NAME. /* Make a copy of command.
Otherwise there's a race between the caller and load(). */ Otherwise there's a race between the caller and load(). */
fn_copy = palloc_get_page (0); cmd_copy = palloc_get_page (0);
if (fn_copy == NULL) if (cmd_copy == NULL)
return TID_ERROR; return TID_ERROR;
strlcpy (fn_copy, file_name, PGSIZE);
/* Create a new thread to execute FILE_NAME. */ /* Imposing implicit limit that the command line arguments
tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy); including the user program name fit within a single page. */
strlcpy (cmd_copy, cmd, PGSIZE);
/* Retrieve first argument of command, which is the file name
of the process. */
char *file_name = strtok_r (cmd_copy, " ", &data.cmd_saveptr);
/* NOTE: Currently, the file being executed is closed in load () and then
reopened here. Because load is an exported public function, this
might be necessary. */
lock_acquire (&filesys_lock);
/* Validates that the current file to be executed is a valid file */
bool valid_file = filesys_open (file_name) != NULL;
lock_release (&filesys_lock);
if (!valid_file)
return TID_ERROR;
/* Create a new thread to execute the command, by initializing
it running the function 'start_process' with the appropriate
arguments. For details of arguments, see 'start_process'. */
data.cmd = cmd_copy;
strlcpy (data.file_name, file_name, FNAME_MAX_LEN + 1);
sema_init (&data.load_sema, 0);
data.success = false;
tid = thread_create (file_name, PRI_DEFAULT, start_process, &data);
if (tid == TID_ERROR) if (tid == TID_ERROR)
palloc_free_page (fn_copy); {
palloc_free_page (cmd_copy);
}
else
{
sema_down (&data.load_sema);
if (!data.success)
tid = TID_ERROR;
}
return tid; return tid;
} }
/* A thread function that loads a user process and starts it static bool install_page (void *upage, void *kpage, bool writable);
running. */ static bool process_init_stack (char *cmd_saveptr, void **esp, char *file_name);
static void *push_to_stack (void **esp, void *data, size_t data_size);
#define push_var_to_stack(esp, var) (push_to_stack (esp, &var, sizeof (var)))
/* Make the current thread execute 'cmd', passing in a copy of the
command string used for processing, the saveptr used by strtok_r
(in order to further tokenize the same command and retrieve its
arguments), as well as the name of the file being executed. This
involves loading the specified file and starting it running. */
static void static void
start_process (void *file_name_) start_process (void *proc_start_data)
{ {
char *file_name = file_name_;
struct intr_frame if_; struct intr_frame if_;
bool success;
struct process_start_data *data = proc_start_data;
/* Initialize interrupt frame and load executable. */ /* Initialize interrupt frame and load executable. */
memset (&if_, 0, sizeof if_); memset (&if_, 0, sizeof if_);
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
if_.cs = SEL_UCSEG; if_.cs = SEL_UCSEG;
if_.eflags = FLAG_IF | FLAG_MBS; if_.eflags = FLAG_IF | FLAG_MBS;
success = load (file_name, &if_.eip, &if_.esp);
lock_acquire (&filesys_lock);
/* Prevent writing to the file being executed. */
struct file *exec_file = filesys_open (data->file_name);
thread_current ()->exec_file = exec_file;
file_deny_write (exec_file);
lock_release (&filesys_lock);
/* If load failed, quit. */ data->success = load (data->file_name, &if_.eip, &if_.esp);
palloc_free_page (file_name);
if (!success) /* If load failed, free process startup data and quit. */
thread_exit (); if (!data->success)
{
palloc_free_page (data->cmd);
sema_up (&data->load_sema);
thread_exit ();
}
/* Initialize user process stack and free page used to store the
command that executed the process. */
bool success = process_init_stack (data->cmd_saveptr, &if_.esp, data->file_name);
palloc_free_page (data->cmd);
data->success = success;
sema_up (&data->load_sema);
/* If stack initialization failed, free process resources and quit. */
if (!success)
{
thread_exit ();
}
/* Start the user process by simulating a return from an /* Start the user process by simulating a return from an
interrupt, implemented by intr_exit (in interrupt, implemented by intr_exit (in
@@ -76,6 +179,104 @@ start_process (void *file_name_)
NOT_REACHED (); NOT_REACHED ();
} }
/* Helper function that initializes the stack of a newly created
user process. Returns true if successful, false otherwise. */
static bool
process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
{
/* Load command line argument *data* to user process stack.
This can't cause overflow due to enforcing that the size of
command line input must fit in a page. Also keep track
of pointers to the argument data within a linked list. */
struct list arg_list;
list_init (&arg_list);
char *arg = file_name;
int arg_count = 0;
while (arg != NULL)
{
push_to_stack (esp, arg, (strlen (arg) + 1) * sizeof (char));
struct arg_elem *arg_elem = malloc (sizeof (struct arg_elem));
if (arg_elem == NULL)
{
printf("ERROR: Couldn't allocate argument pointer memory for %s!\n",
thread_current ()->name);
return false;
}
arg_elem->arg = *esp;
list_push_front (&arg_list, &arg_elem->elem);
arg_count++;
arg = strtok_r (NULL, " ", &cmd_saveptr);
}
/* Calculate the remaining number of bytes that need to be written
to the user process stack in order to check for possible overflow. */
size_t align_size = ((unsigned int) *esp % WORD_SIZE) * sizeof (uint8_t);
size_t argv_data_size = (arg_count + 1) * sizeof (char *);
size_t argv_size = sizeof (char **);
size_t argc_size = sizeof (int);
size_t return_addr_size = sizeof (void *);
size_t remaining_size = align_size + argv_data_size + argv_size + argc_size
+ return_addr_size;
/* If pushing the rest of the data required for the stack would cause
overflow, allocate an extra page that is contiguous within the
virtual address space (below the current address range). */
if (PHYS_BASE - *esp + remaining_size > PGSIZE)
{
uint8_t *kpage = palloc_get_page (PAL_USER | PAL_ZERO);
if (!install_page (((uint8_t *) PHYS_BASE) - PGSIZE * 2, kpage, true))
return false;
}
/* Align stack pointer to word size before pushing argv elements for
performance. */
*esp -= align_size;
/* Push a null pointer sentinel inside argv. */
char *null_sentinel = NULL;
push_var_to_stack (esp, null_sentinel);
/* Push pointers to process arguments from argument linked list */
struct list_elem *e = list_begin (&arg_list);
struct list_elem *tail = list_tail (&arg_list);
while (e != tail)
{
struct arg_elem *arg_elem = list_entry (e, struct arg_elem, elem);
push_var_to_stack(esp, arg_elem->arg);
e = list_next (e);
free (arg_elem);
}
/* Push pointer to the start of argv array. */
char **argv = *esp;
push_var_to_stack(esp, argv);
/* Push the number of arguments to the stack. */
push_var_to_stack(esp, arg_count);
/* Push fake return address (null pointer). */
push_var_to_stack (esp, null_sentinel);
return true;
}
/* Helper function that pushes the first 'data_size' bytes stored
in the address '*data' into the stack given a pointer to the
stack pointer '**esp'. */
static void *
push_to_stack (void **esp, void *data, size_t data_size)
{
*esp -= data_size;
memcpy (*esp, data, data_size);
return *esp;
}
/* Waits for thread TID to die and returns its exit status. /* Waits for thread TID to die and returns its exit status.
* If it was terminated by the kernel (i.e. killed due to an exception), * If it was terminated by the kernel (i.e. killed due to an exception),
* returns -1. * returns -1.
@@ -88,16 +289,110 @@ start_process (void *file_name_)
int int
process_wait (tid_t child_tid UNUSED) process_wait (tid_t child_tid UNUSED)
{ {
return -1; struct process_result *child_result = NULL;
struct list_elem *e;
struct thread *cur = thread_current ();
for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results); e = list_next (e))
{
struct process_result *result
= list_entry (e, struct process_result, elem);
if (result->tid == child_tid)
{
child_result = result;
break;
}
/* List is ordered, allowing us to break early. */
else if (result->tid > child_tid)
break;
}
if (child_result == NULL)
{
return -1;
}
/* Wait for child to die. */
sema_down (&child_result->sema);
/* We need lock release in process_exit, so we need to acquire (and possibly
wait) for it here to ensure we don't free the lock memory before it is
released in process_exit. */
lock_acquire (&child_result->lock);
/* To prevent waiting for child twice, remove it from the list.
No need to use lock since this is the only thread with access to
the struct process_result now. */
list_remove (&child_result->elem);
int exit_status = child_result->exit_status;
lock_release (&child_result->lock);
free (child_result);
return exit_status;
} }
/* Free the current process's resources. */ /* Free the current process's resources. */
void void
process_exit (void) process_exit (void)
{ {
struct thread *cur = thread_current (); struct thread *cur = thread_current ();
uint32_t *pd; uint32_t *pd;
printf ("%s: exit(%d)\n", cur->name, cur->exit_status);
/* Clean up all open files */
hash_destroy (&cur->open_files, fd_cleanup);
/* Close the executable file. */
if (cur->exec_file != NULL)
{
lock_acquire (&filesys_lock);
file_close (cur->exec_file);
lock_release (&filesys_lock);
}
/* Update process result. */
if (cur->result != NULL)
{
lock_acquire (&cur->result->lock);
cur->result->exit_status = cur->exit_status;
/* Parent has died, child has to free the struct process_result * */
if (sema_try_down (&cur->result->sema))
{
lock_release (&cur->result->lock);
free (cur->result);
}
/* Parent is still alive and will be the one to free the
struct process_result *, and may be waiting so call sema_up */
else
{
sema_up (&cur->result->sema);
lock_release (&cur->result->lock);
}
}
/* Free child process results or signal parent's death. */
struct list_elem *e;
for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results);)
{
struct process_result *result
= list_entry (e, struct process_result, elem);
struct list_elem *next = list_next (e);
lock_acquire (&result->lock);
/* Child has died (and was not waited for). Free the result. */
if (sema_try_down (&result->sema))
{
lock_release (&result->lock);
free (result);
}
/* Child is still alive, signal via sema that parent has died. */
else
{
sema_up (&result->sema);
lock_release (&result->lock);
}
e = next;
}
/* Destroy the current process's page directory and switch back /* Destroy the current process's page directory and switch back
to the kernel-only page directory. */ to the kernel-only page directory. */
pd = cur->pagedir; pd = cur->pagedir;
@@ -214,6 +509,7 @@ load (const char *file_name, void (**eip) (void), void **esp)
off_t file_ofs; off_t file_ofs;
bool success = false; bool success = false;
int i; int i;
lock_acquire (&filesys_lock);
/* Allocate and activate page directory. */ /* Allocate and activate page directory. */
t->pagedir = pagedir_create (); t->pagedir = pagedir_create ();
@@ -313,13 +609,12 @@ load (const char *file_name, void (**eip) (void), void **esp)
done: done:
/* We arrive here whether the load is successful or not. */ /* We arrive here whether the load is successful or not. */
file_close (file); file_close (file);
lock_release (&filesys_lock);
return success; return success;
} }
/* load() helpers. */ /* load() helpers. */
static bool install_page (void *upage, void *kpage, bool writable);
/* Checks whether PHDR describes a valid, loadable segment in /* Checks whether PHDR describes a valid, loadable segment in
FILE and returns true if so, false otherwise. */ FILE and returns true if so, false otherwise. */
static bool static bool

View File

@@ -1,20 +1,428 @@
#include "userprog/syscall.h" #include "userprog/syscall.h"
#include "devices/shutdown.h"
#include "devices/input.h"
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "threads/vaddr.h"
#include "threads/interrupt.h"
#include "threads/malloc.h"
#include "threads/thread.h"
#include "threads/synch.h"
#include "userprog/process.h"
#include "userprog/pagedir.h"
#include <stdio.h> #include <stdio.h>
#include <syscall-nr.h> #include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h" static unsigned fd_counter = MIN_USER_FD;
struct open_file
{
int fd; /* File Descriptor / Identifier */
struct file *file; /* Pointer to the associated file */
struct hash_elem elem; /* elem for a hash table */
};
static void syscall_handler (struct intr_frame *); static void syscall_handler (struct intr_frame *);
/* A syscall_function is a function that receives up to 3 arguments, the
arguments to the functions are either ints or pointers taking up to 32 bits
in size. */
typedef uintptr_t (*syscall_function) (uintptr_t, uintptr_t, uintptr_t);
/* System call function prototypes */
static void syscall_halt (void);
static void syscall_exit (int status);
static pid_t syscall_exec (const char *cmd_line);
static int syscall_wait (pid_t pid);
static bool syscall_create (const char *file, unsigned initial_size);
static bool syscall_remove (const char *file);
static int syscall_open (const char *file);
static int syscall_filesize (int fd);
static int syscall_read (int fd, void *buffer, unsigned size);
static int syscall_write (int fd, const void *buffer, unsigned size);
static void syscall_seek (int fd, unsigned position);
static unsigned syscall_tell (int fd);
static void syscall_close (int fd);
static struct open_file *fd_get_file (int fd);
static void *validate_user_pointer (const void *ptr, size_t size);
/* A struct defining a syscall_function pointer along with its arity. */
typedef struct
{
syscall_function function; /* Function pointer. */
int arity; /* Number of arguments of the function. */
} syscall_arguments;
/* A look-up table mapping numbers to system call functions with their number of
arguments. */
static const syscall_arguments syscall_lookup[] =
{
[SYS_HALT] = {(syscall_function) syscall_halt, 0},
[SYS_EXIT] = {(syscall_function) syscall_exit, 1},
[SYS_EXEC] = {(syscall_function) syscall_exec, 1},
[SYS_WAIT] = {(syscall_function) syscall_wait, 1},
[SYS_CREATE] = {(syscall_function) syscall_create, 2},
[SYS_REMOVE] = {(syscall_function) syscall_remove, 1},
[SYS_OPEN] = {(syscall_function) syscall_open, 1},
[SYS_FILESIZE] = {(syscall_function) syscall_filesize, 1},
[SYS_READ] = {(syscall_function) syscall_read, 3},
[SYS_WRITE] = {(syscall_function) syscall_write, 3},
[SYS_SEEK] = {(syscall_function) syscall_seek, 2},
[SYS_TELL] = {(syscall_function) syscall_tell, 1},
[SYS_CLOSE] = {(syscall_function) syscall_close, 1},
};
/* The number of syscall functions (i.e, number of elements) within the
syscall_lookup table. */
static const int LOOKUP_SIZE
= sizeof (syscall_lookup) / sizeof (syscall_arguments);
/* Initialises the syscall handling system, as well as a global lock to
synchronise all file access between processes. */
void void
syscall_init (void) syscall_init (void)
{ {
intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall"); intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
lock_init (&filesys_lock);
}
/* Function that takes a interrupt frame containing a syscall and its args.
Validates the arguments and pointers before calling the relevant
high-level system call function, storing its output (if any) in f->eax */
static void
syscall_handler (struct intr_frame *f)
{
/* First, read the system call number from the stack. */
validate_user_pointer (f->esp, 1);
unsigned syscall_number = *(int *) f->esp;
/* Ensures the number corresponds to a system call that can be handled. */
if (syscall_number >= LOOKUP_SIZE)
thread_exit ();
syscall_arguments syscall = syscall_lookup[syscall_number];
/* Next, read and copy the arguments from the stack pointer. */
validate_user_pointer (f->esp + sizeof (uintptr_t),
syscall.arity * sizeof (uintptr_t));
uintptr_t args[3] = {0};
for (int i=0; i < syscall.arity; i++)
args[i] = *(uintptr_t *) (f->esp + sizeof (uintptr_t) * (i + 1));
/* Call the function that handles this system call with the arguments. When
there is a return value it is stored in f->eax. */
f->eax = syscall.function (args[0], args[1], args[2]);
}
/* Called upon a "halt" syscall, resulting in a complete shutdown of the
process, via shutdown_power_off (); */
static void
syscall_halt (void)
{
shutdown_power_off ();
} }
static void static void
syscall_handler (struct intr_frame *f UNUSED) syscall_exit (int status)
{ {
printf ("system call!\n"); /* Sets exit_status of the thread to status. thread_exit () will call
process_exit () if user programs are allowed. */
thread_current ()->exit_status = status;
thread_exit (); thread_exit ();
} }
/* Executes a given command with the relevant args, by calling process_execute.
Acquires the filesystem lock as process_execute accesses the file system.
Returns PID for the process that is running the CMD_LINE
*/
static pid_t
syscall_exec (const char *cmd_line)
{
validate_user_pointer (cmd_line, 1);
pid_t pid = process_execute(cmd_line);
return pid;
}
/* Handles the syscall of wait. Effectively a wrapper for process_wait as the
necessary validation and such all happens in process_wait anyway. */
static int
syscall_wait (pid_t pid)
{
return process_wait (pid);
}
/* Handles the syscall for file creation. First validates the user file
pointer. Acquires the file system lock to prevent synchronisation issues,
and then uses FILESYS_CREATE to create the file, returning the same status */
static bool
syscall_create (const char *file UNUSED, unsigned initial_size UNUSED)
{
validate_user_pointer (file, 1);
lock_acquire (&filesys_lock);
bool status = filesys_create (file, initial_size);
lock_release (&filesys_lock);
return status;
}
/* Handles the syscall for file removal. First validates the user file pointer.
Acquires the file system lock to prevent synchronisation issues, and then
uses FILESYS_REMOVE to remove the file, returning the same success status */
static bool
syscall_remove (const char *file)
{
validate_user_pointer (file, 1);
lock_acquire (&filesys_lock);
bool status = filesys_remove (file);
lock_release (&filesys_lock);
return status;
}
/* Handles the syscall for opening a file connection. First, validates the file
pointer. Then it acquires a lock for the file system, in order to open the
connection without synchronisation issues. It then maps a new fd to this file
in the hash table before returning the fd. */
static int
syscall_open (const char *file)
{
validate_user_pointer (file, 1);
lock_acquire (&filesys_lock);
struct file *ptr = filesys_open (file);
lock_release (&filesys_lock);
if (ptr == NULL)
return -1;
/* Allocate space for a struct representing a mapping from an FD to a struct
file. */
struct open_file *file_info
= (struct open_file*) malloc (sizeof (struct open_file));
if (file_info == NULL)
return -1;
/* Populate the above struct, with a unique FD and the current open file */
file_info->fd = fd_counter++;
file_info->file = ptr;
/* Add the new FD->file mapping to the hashtable for the current thread */
hash_insert (&thread_current ()->open_files, &file_info->elem);
/* Return the new FD */
return file_info->fd;
}
/* Handles the syscall for getting a file's size. Converts a provided FD into
the asssociated file struct. Acquire the lock for the filesystem and use
FILE_LENGTH to calculate the length for return. */
static int
syscall_filesize (int fd)
{
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return -1;
lock_acquire (&filesys_lock);
int bytes = file_length (file_info->file);
lock_release (&filesys_lock);
return bytes;
}
/* Handles the syscall for reading SIZE bytes from a file referenced by FD.
If the FD references the console, use input_getc (), otherwise convert the
FD to its associated file struct, acquire the filesystem lock, read up to
SIZE bytes and then return the number of bytes read.*/
static int
syscall_read (int fd, void *buffer, unsigned size)
{
/* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are
allowed. */
if (fd < 0 || fd == STDOUT_FILENO)
return -1;
validate_user_pointer (buffer, size);
if (fd == STDIN_FILENO)
{
/* Reading from the console. */
char *write_buffer = buffer;
for (int i = 0; i < size; i++)
write_buffer[i] = input_getc ();
return size;
}
else
{
/* Reading from a file. */
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return -1;
lock_acquire (&filesys_lock);
int bytes_written = file_read (file_info->file, buffer, size);
lock_release (&filesys_lock);
return bytes_written;
}
}
/* Handles the syscall for writing SIZE bytes to a file referenced by FD.
If the FD references the console, use put_buf (), otherwise convert the
FD to its associated file struct, acquire the filesystem lock, write up to
SIZE bytes and then return the number of bytes written.*/
static int
syscall_write (int fd, const void *buffer, unsigned size)
{
/* Only console (fd = 1) or other files, not including STDIN, (fd > 1) are
allowed. */
if (fd <= 0)
return 0;
validate_user_pointer (buffer, size);
if (fd == STDOUT_FILENO)
{
/* Writing to the console. */
putbuf (buffer, size);
return size;
}
else
{
/* Writing to a file. */
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return 0;
lock_acquire (&filesys_lock);
int bytes = file_write (file_info->file, buffer, size);
lock_release (&filesys_lock);
return bytes;
}
}
/* Handles the syscall for seeking to POSITION bytes in a file referenced by
FD. Converts the FD to its associated file struct, acquires the filesystem
lock and then uses file_seek to adjust the cursor to a specific position in
the file.*/
static void
syscall_seek (int fd, unsigned position)
{
struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL)
{
lock_acquire (&filesys_lock);
file_seek (file_info->file, position);
lock_release (&filesys_lock);
}
}
/* Handles the syscall for returning the next byte in a file referenced by
FD. Converts the FD to its associated file struct, acquires the filesystem
lock and then uses file_tell to read the next byte.*/
static unsigned
syscall_tell (int fd)
{
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return 0;
lock_acquire (&filesys_lock);
unsigned pos = file_tell (file_info->file);
lock_release (&filesys_lock);
return pos;
}
/* Handles the syscall for closing a connection to a file. Converts the FD to
its associated file struct. If it exists, it removes it from the hash table,
acquires the filesystem lock, and uses file_close to close the connection.*/
static void
syscall_close (int fd)
{
struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL)
{
hash_delete (&thread_current ()->open_files, &file_info->elem);
lock_acquire (&filesys_lock);
file_close (file_info->file);
lock_release (&filesys_lock);
free (file_info);
}
}
/* Hashing function needed for the open_file table. Returns a hash for an entry,
based on its FD. */
unsigned
fd_hash (const struct hash_elem *element, void *aux UNUSED)
{
return hash_int (hash_entry (element, struct open_file, elem)->fd);
}
/* Comparator function for the open_file table. Compares two entries based on
the FDs. */
bool
fd_less (const struct hash_elem *a_, const struct hash_elem *b_,
void *aux UNUSED)
{
struct open_file *a = hash_entry (a_, struct open_file, elem);
struct open_file *b = hash_entry (b_, struct open_file, elem);
return a->fd < b->fd;
}
/* Function to clean up an open file entry. Closes the file and frees the
associated memory. */
void
fd_cleanup (struct hash_elem *e, void *aux UNUSED)
{
struct open_file *file_info = hash_entry (e, struct open_file, elem);
lock_acquire (&filesys_lock);
file_close (file_info->file);
lock_release (&filesys_lock);
free (file_info);
}
/* Gets a file from its descriptor (FD number). If there is no file with the fd
FD it returns NULL. */
static struct open_file *
fd_get_file (int fd)
{
/* We have to set up a fake open_file in order to be able to search the hash
table. See hash.h. */
struct open_file fake_file_info;
fake_file_info.fd = fd;
struct hash_elem *e
= hash_find (&thread_current ()->open_files, &fake_file_info.elem);
if (e == NULL)
return NULL;
return hash_entry (e, struct open_file, elem);
}
/* Validates if a block of memory starting at PTR and of size SIZE bytes is
fully contained within user virtual memory. Kills the thread (by calling
thread_exit) if the memory is invalid. Otherwise, returns the PTR given.
If the size is 0, the function does no checks and returns PTR.*/
static void *
validate_user_pointer (const void *ptr, size_t size)
{
if (size > 0 && (ptr == NULL ||
!is_user_vaddr (ptr) ||
!is_user_vaddr (ptr + size - 1) ||
pagedir_get_page (thread_current()->pagedir, ptr) == NULL))
thread_exit ();
return (void *) ptr;
}

View File

@@ -1,6 +1,19 @@
#ifndef USERPROG_SYSCALL_H #ifndef USERPROG_SYSCALL_H
#define USERPROG_SYSCALL_H #define USERPROG_SYSCALL_H
#include <hash.h>
#include "threads/synch.h"
#define MIN_USER_FD 2
typedef int pid_t;
struct lock filesys_lock;
void syscall_init (void); void syscall_init (void);
unsigned fd_hash (const struct hash_elem *element, void *aux);
bool fd_less (const struct hash_elem *a, const struct hash_elem *b, void *aux);
void fd_cleanup (struct hash_elem *e, void *aux);
#endif /* userprog/syscall.h */ #endif /* userprog/syscall.h */