summaryrefslogtreecommitdiffstats
path: root/notes
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2012-03-27 11:51:08 +0200
committermanuel <manuel@mausz.at>2012-03-27 11:51:08 +0200
commit4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b (patch)
tree868c52e06f207b5ec8a3cc141f4b8b2bdfcc165c /notes
parenteae0bd57f0a26314a94785061888d193d186944a (diff)
downloadprogos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.gz
progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.bz2
progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.zip
reorganize file structure to match the upstream requirements
Diffstat (limited to 'notes')
-rw-r--r--notes/1.txt81
-rw-r--r--notes/2.txt164
-rw-r--r--notes/3.txt241
3 files changed, 486 insertions, 0 deletions
diff --git a/notes/1.txt b/notes/1.txt
new file mode 100644
index 0000000..9d478f7
--- /dev/null
+++ b/notes/1.txt
@@ -0,0 +1,81 @@
1Getting Started with PINTOS
2===========================
3
4Building Project 1
5------------------
6
7pintos $ cd src/threads
8threads $ make
9
10
11Building Bochs
12--------------
13You should have a patched bochs install available.
14
15See
16
17 http://courses.mpi-sws.org/os-ss11/assignments/pintos/pintos_12.html#SEC160
18
19There is a build script src/misc/bochs-2.3.7-build.sh in the pintos fork from Saarland,
20which (after small modifications) works on a modern Ubuntu x86.
21
22For Ubuntu 11 with a Linux 3.0 kernel, you need to:
23
24 * Regenerate the configure script (autoconf configure.in)
25 * Patch the test for Linux 2.4 or 2.6
26
27After building, copy bochs and bochs-gdb to the pintos/src/utils directory
28
29Running
30-------
31
32 # [pintos/src]
33 PATH=`pwd`/utils:$PATH
34
35 cd threads/build
36 # [pintos/src/threads/build]
37 pintos run alarm-multiple > logfile
38
39
40### Reproducability
41
42This command line flags to pintos influence reproducability.
43Remember: you need the patched bochs build.
44
45 -j seed ... Reproducible behavior
46 -r ... Real-Time behavior
47
48Running with qemu
49-----------------
50
51 # [pintos/src]
52 vim utils/pintos # comment line with -no-kqemu flag
53
54 cd threads/build
55 # [pintos/src/threads/build]
56 pintos --qemu -- run alarm-multiple
57
58Debugging
59---------
60
61pintos $ vim utils/pintos-gdb
62
63 GDBMACROS=${PINTOS_SRC}/misc/gdb-macros
64
65[pts/0 build] $ pintos --gdb -- run alarm-multiple
66[pts/1 build] $ pintos-gdb kernel.o
67(gdb) debugpintos
68
69Testing
70-------
71
72* Running all tests
73
74 build $ make check
75
76* Running a single test
77
78 build $ rm tests/threads/alarm-multiple.result
79 build $ make tests/threads/alarm-multiple.result
80
81
diff --git a/notes/2.txt b/notes/2.txt
new file mode 100644
index 0000000..c81b980
--- /dev/null
+++ b/notes/2.txt
@@ -0,0 +1,164 @@
1Projekt 1 - Threads
2===================
3
4alarm clock
5-----------
6The simplest strategy is to maintain a wait list for
7all threads blocked for sleep.
8
9 * In 'timer_interrupt', check for threads which can be
10 unblocked from sleeping
11 * In 'sleep', set sleep timeout in thread, block the
12 thread and put it on the sleep list
13
14Notes:
15
16 * There are three places where a thread is added to the
17 ready list:
18 - thread_init
19 - thread_yield
20 - thread_unblock
21 * Iterate list with removal:
22 for (e = list_begin (&list); e != list_end (&list); )
23 if(...)
24 e = list_remove(e)->prev;
25 /* Unblock must be called AFTER removing, as thread.elem is reused */
26 else
27 e = list_next(e);
28
29Stats:
30
31 pintos/src/devices/timer.c | 40 ++++++++++++++++++++++++++++++++++++++--
32 pintos/src/threads/thread.h | 3 +++
33 2 files changed, 41 insertions(+), 2 deletions(-)
34
35 Design & Implementation time: 4 hours
36
37Priority Scheduler
38------------------
39
40A simple implementation of the priority scheduler (64 priority levels, round robin within
41one priority group).
42
43 * If a new task arrives with a higher priority, switch to this group
44 * If the currently active group is empty, search for the group with the next highest priority
45
46Notes:
47
48 * thread_{init,unblock,yield} now call thread_ready, which updates the lowest ready priority
49 * The thread_unblock operation does not yield a new thread immediately. Therefore, we need to check
50 later whether we need to switch to a higher priority thread (via thread_yield).
51 As thread_unblock is called with interrupts off, it seemed best to perform
52 this check when interrupts are enabled. This is only necessary if a higher priority task
53 is ready.
54 * First attempt passed alarm-priority, but failed to pass the priority-preempt test.
55 But the debugging facilities are fantastic, so it was easy to spot the problem
56 * Wolfgang suggested to yield a software interrupt when unblocking instead of modifying
57 interrupt_enable.
58
59Stats:
60
61 pintos/src/threads/interrupt.c | 3 +-
62 pintos/src/threads/thread.c | 60 ++++++++++++++++++++++++++++++++--------
63 pintos/src/threads/thread.h | 1 +
64 3 files changed, 51 insertions(+), 13 deletions(-)
65
66 Design and implementation time: 3 hours
67
68Priority Locks
69--------------
70
71We also need to select higher priority task first from locks, semaphores and condition variables.
72This easiest implementation searches for the thread with the highest priority in the wait queue.
73
74Notes:
75
76 * It is sufficient to implement the priority based selection twice, for sema_up and
77 cond_signal. cond_signal is a little bit harder, as we need to store the priority
78 (or the waiting thread) in the semaphore_elem type
79 * There are some handy list utility functions; in this case, list_max does a fine job
80 for both sema_up and cond_signal
81 * It is difficult to implement this in an efficient (sublinear) way, because priority donation
82 may boost a thread at any time!
83
84Stats:
85
86 pintos/src/threads/synch.c | 40 ++++++++++++++++++++++++++++++++++------
87 1 files changed, 34 insertions(+), 6 deletions(-)
88
89 Design and Implementation time: 1 hour
90
91Priority Donation
92-----------------
93If a thread aquires a lock, the lock holder needs to be boosted to the donated priority.
94We need to deal with nesting and chaining:
95
96 * Lock/Thread correspondence: Each lock is associated with at most one thread that holds it.
97 Therefore, donated priority can be associated with a lock.
98 * If a thread t wants to obtain a lock L, and a thread with a lower priority holds it,
99 the thread holding the lock is boosted to the priority of the requesting thread
100 * Chaining: If the boosted thread is also blocked on a lock, than we also need to donate
101 the priority to that lock, in a transitive way.
102 * Nesting: If a thread may hold more than one lock, we need to keep track of the donation
103 to each lock. When a lock is released or the static priority changes, the highest priority
104 donated to other locks is assigned to the thread.
105
106With this information, the following rules seem suitable (without proof of correctness):
107
108 * If thread T tries to aquire lock L
109
110 ==> if(L.owner)
111 T.locked_on = L
112 donate_priority (L, T.priority)
113 end
114 donate_priority (L ,p) :=
115 L.priority v= p
116 L.holder.priority v= p
117 donate_priority( L.holder.locked_on, p)
118 end
119
120 * If a thread T aquires a lock L
121
122 ==> L.holder = T
123 T.locks += L
124 T.lock_on = none
125
126
127 * If a thread T releases a lock L
128
129 ==> L.holder = none
130 T.locks -= L
131 T.priority = max (T.locks.priority, static_priority)
132
133To implement this, each thread needs to maintain a list of locks, a static priority,
134and a reference to the lock blocking it.
135
136Notes:
137
138 * Difficult to design, really a challenge.
139 Literature (short paper) could be helpful.
140 Maybe it would be interesting to ask for correctness proof sketches?
141
142 * First design was wrong, because I assumed locks are aquired in FIFO fashion
143 * First implementation failed to pass donate tests, due to a typo. Debugging
144 facilities are fantastic, no problem to spot this one.
145 * Next try, priority-donate-lower failed. Looking at the source code revealed that
146 we need to recompute the priority at the end of thread_set_priority.
147 * Next try, priority-donate-chain failed. Chaining is tricky to get right;
148 in my implementation, chained donations were lost after one lock was released.
149
150 * It would be an interesting option to ask for new test cases from the students
151 * I think it would also be a cool task to write a test for a RMS scheduling
152 scenario with blocking.
153
154Stats:
155
156 pintos/src/threads/synch.c | 15 ++++++++++++
157 pintos/src/threads/synch.h | 6 +++-
158 pintos/src/threads/thread.c | 53 +++++++++++++++++++++++++++++++++++++++++-
159 pintos/src/threads/thread.h | 9 ++++++-
160 pintos/src/utils/pintos | 2 +-
161
162 Design: 5h
163 Implementation: 3h
164
diff --git a/notes/3.txt b/notes/3.txt
new file mode 100644
index 0000000..bc64f88
--- /dev/null
+++ b/notes/3.txt
@@ -0,0 +1,241 @@
1Project 2
2=========
3
4Working with Disks
5------------------
6
7Assumes you ran make in src/userprog and src/examples.
8
9 * Create a 2 MB hard disk for pintos
10
11 # [src/userprog/build]
12 pintos-mkdisk filesys.dsk --filesys-size=2
13
14 * Format Disk
15
16 # -f ... format virtual disk
17 pintos -f -q
18
19 * Copy file to filesystem
20
21 # -p FILE ... file to put on virtual disk
22 # -a FILE ... newname on virtual disk
23 pintos -p ../../examples/echo -a echo -- -q
24
25 * Execute echo, and get file 'echo' from the virtual disk
26
27 pintos -g echo -- -q run 'echo x'
28
29Putting all together, we can run an minimal example like that:
30
31 # [src/userprog/build]
32 pintos --filesys-size=2 -p ../../examples/halt -a halt -- -f -q run 'halt'
33
34Getting Started
35---------------
36
37 * Fix the problem with the .note.gnu.build-id segment
38
39 * Change the stack setup in process.c#setup_stack() to
40
41 *esp = PHYS_BASE - 12;
42
43 * Change process_wait() to an infinite loop
44
45This should be enough to see 'system call!' when executing
46the 'halt' example.
47
48Next, we need to implement user memory access and the
49the system call dispatcher, as well as the basic
50system calls halt, exit and write.
51
52A simple implementation of user memory access first checks
53whether the address is in user space, and the calls load_page.
54
55For an initial system call dispatcher, we convert the stack pointer
56saved by the processor during the interrupt to kernel space, and
57then dispatch to halt, exit and write. For now, exit just terminates
58the process, and write uses printf, ignoring the fd argument.
59The return value is stored into %eax.
60
61Notes:
62 * halt(): There is no function shutdown() in init.h, only
63 shutdown_poweroff in shutdown.h
64
65 * When accessing data from user space in kernel space, we need to be
66 sure that the entire address ranged accessed is in user space.
67 Note that pointers are not necessarily aligned, and thus might
68 involve two user pages.
69 Furthermore, buffers need to be copied to kernel space;
70 otherwise, concurrent user space operations could corrupt the kernel.
71 Linux allows at most one kernel page for such buffers; we follow
72 the same route.
73
74 * Debugging: the function hex_dump() is useful; no need to
75 reimplement it.
76
77 * Something went wrong with the write system call, and this
78 is rather tricky to debug.
79 I invoked the system call directly, using inline
80 assembly; this worked fine?
81 Then I tried to debug the user space program; to this
82 end, lookup the code address you are interested in,
83 and use gdb together with objdump for debugging:
84
85 Debugging 'write(1,"USA\n",4)'
86
87 break *0x0804820e # break at <write>
88 cont # pushl 0xc(%esp)
89 info registers # esp = 0xbfffffbc
90 x/1w (0xbfffffbc+0xc) # ==> 4 (length)
91 stepi # pushl 0x8(%esp)
92 info registers # esp = 0x......b8
93 x/1w 0xbfffffb8 # ==> 4 (TOS)
94 x/1w (0xbfffffb8+8) # ==> 1 (wrong) !!!
95
96 Apparently, the inline assembler in pintos does not use
97 the right constraints.
98
99Stat:
100 pintos/src/lib/user/syscall.c | 6 +-
101 pintos/src/userprog/process.c | 5 ++-
102 pintos/src/userprog/syscall.c | 92 ++++++++++++++++++++++++++++++++++++++--
103
104 Reading and Implementation Time: 6 hours
105 Debugging Syscalls: 5 hours
106
107
108Argument Passing
109----------------
110First, we tokenize the command using strtok_r, and then setup
111the stack.
112
113Notes:
114 * As noted in the doc, just using strtok_r seems fine.
115 However, as strtok_r modifies the string even if only
116 the first token is needed, some copying is involved
117 if it is used to obtain the filename.
118 * Due to the detailed description in the documentation,
119 setting up the stack is mostly implementation work.
120 * One of the trickier implementation aspects is that we
121 modify the stack in kernel space, but need to convert
122 pointers to user space before pushing them on the stack.
123 * Debugging: Optimizations were really troublesome debugging
124 this task; making setup_stack non-static at least helped
125 to set a suitable breakpoint. In the end, printf was the
126 better debugging aid for this task.
127
128Stat:
129 pintos/src/userprog/process.c | 116 +++++++++++++++++++++++++++++++++--------
130
131 Design and Implementation Time: 4 hours
132
133
134Process Management: exec, wait and exit
135---------------------------------------
136The wait system call requires that all children
137of a process are known, that the exit code of
138a process is stored until collected by the parent,
139and that the parent can block until the child
140process terminates.
141
142One difficult aspect in the design is that kernel
143threads are not processes, and that child threads
144may exit after their parent. It is important to
145note that threads do not need to wait for their
146children, but that we need to keep the exit status
147until the parent exits.
148
149In the original design, a thread is cleaned up when
150in the scheduler right after it died. In our design
151we delay the cleanup if the parent thread is still alive.
152
153Another issue is that thread_create needs to block
154until the load process of the child thread has finished.
155
156Notes:
157 * I wanted to use the same semaphore for startup and wait.
158 This works, but we need one additional variable (or bit)
159 to distinguish failure at load time from failure at
160 runtime.
161 * Ugly 1: thread_create only gives back a tid,
162 so it is not possible to directly access the semaphore
163 in process_execute. Therefore we need to iterate over the
164 child list (which is not that bad, because if loading failed,
165 the child needs to be removed from the list anyway).
166 * Ugly 2: We up the semaphore used to synchronize
167 with process_execute and process_wait in thread.c, for
168 all threads.
169 * As also noted by rene, it is important to identifiy memory leaks,
170 as early as possible. To this end, first add debug messages to
171 page_alloc/page_free, and then run test programs to identify leaking
172 pages. Then debug, add conditional breakpoints to stop when a leaking
173 page is allocated, and inspect the stacktrace to find the culprit.
174
175Stats:
176 pintos/src/threads/thread.c | 31 +++++++++++++++++---
177 pintos/src/threads/thread.h | 8 +++++
178 pintos/src/userprog/process.c | 60 ++++++++++++++++++++++++++++++++--------
179 pintos/src/userprog/syscall.c | 19 +++++++++---
180
181 Design and Implementation Time: 7 hours
182
183File I/O System Calls
184---------------------
185For file I/O we need to implement synchronization (filesys is not thread safe).
186The documentation states that it is not recommended to modify the code in
187the filesys directory for now. A very simple solution is to use one lock for all filesystem operations, including process.c#load.
188Furthermore, we need to deny writes to a a file currently running as a user
189space process.
190
191Notes:
192 * init_thread() must not aquire locks, and thus not allocate pages.
193 Otherwise, the initialization of the init thread fails.
194 * The {lg,sm}-full tests failed in the initial implementation;
195 apparently, the read/write system calls should always read/write the
196 full amount of bytes specified to pass this tests. This was not
197 clear from the assignment.
198 * It is not obvious that file_close calls file_allow_write. But an
199 executable should not be writeable during its execution. Therefore,
200 one needs to make sure that it stays write protected after loading
201 has finished. I solve this by keeping the executable open during
202 execution.
203 * The multi-oom test failed again; debugging revealed that I forgot
204 to close all files at process_exit.
205
206Stats:
207
208 pintos/src/threads/thread.c | 1 +
209 pintos/src/threads/thread.h | 6 +-
210 pintos/src/userprog/process.c | 53 ++++-
211 pintos/src/userprog/process.h | 2 +
212 pintos/src/userprog/syscall.c | 435 +++++++++++++++++++++++++++++++-----
213 pintos/src/userprog/syscall.h | 1 +
214 6 files changed, 381 insertions(+), 117 deletions(-)
215 Design and Implementation Time: 8 hours
216
217Improved User Memory Access
218---------------------------
219Looking at Project 3, it is a much better idea to not check whether a user
220space page is valid, but just let the page fault handler do the job.
221I decided to exit the process in the page fault handler if the address
222is in user space. One needs to take care of temporary memory allocated
223by the syscall handler, to avoid memory leaks. To this end, temporary kernel
224pages allocated in the handler are recorded and either freed at the end
225of the syscall or the end of the process.
226
227Notes:
228 * When using this approach, it is vital to copy user buffers
229 before reading or writing. With virtual memory, a page fault may
230 require to access the file system, and thus may cause race
231 conditions during access to the file system
232
233Stats:
234 pintos/src/threads/thread.h | 5 +-
235 pintos/src/userprog/exception.c | 17 ++-
236 pintos/src/userprog/process.c | 2 +-
237 pintos/src/userprog/syscall.c | 314 +++++++++++++++++++--------------------
238 pintos/src/userprog/syscall.h | 2 +-
239 5 files changed, 173 insertions(+), 167 deletions(-)
240
241 Implementation Time: 3 hours