| Friday, November 2, 2001 |
|---|
Question
Simple, short, (probably) stupid question: if a bunch of input lines arrive really fast, do we get one TRAP_TTY_RECEIVE per line, or just one? If the latter, how do we know how many lines are waiting?
Answer:
One per line.
Question
The Sun thr_join takes void **status as its third argument - this make sense since we need to set the value of the void * that points to the user determined status data. Your specification gives the third argument of Thr_join as void *, which confuses me since I'm not sure how you expect us to return the status to the user this way. Is this an error in the specification or am I missing something?
Answer:
void ** does make more sense....but you can use void * to the same effect. Basically this is an error in the spec, it should be void **, but keep to the spec and cast between void ** and void *
Question
What validation do we need to do to ensure that a process does not fiddle with another process's semaphores?
Answer:
Whether you validate or not is up to you, none is required.
Question
Should we be getting a trap memory if all we do is allocate a large array like: char big_buffer[8*PAGESIZE];. This is all we're doing in our test case but it won't generate a trap memory. If we write a single character to this array, then we do get a trap memory.
Answer:
a memory alloc won't cause a trap memory since it only adjusts the stack pointer and doesn't actually touch anything in memory. a trap memory only occurs when you try to access the page (when you write the character)
| Thursday, November 1, 2001 |
|---|
Question
The handout says SemAlloc causes the kernel to allocate a kernel to the CALLING ADDRESS SPACE. We took this to mean that each address space would have a unique semaphore namespace, so if process A and process B do not share a heap, Process A's semaphore #1 would actually refer to a kernel structure distinct from Process B's semaphore #1. On a shared fork, since there is only one address space. Both processes would be able to access semaphores allocated by the other whether or not the semaphores were alloced before or after the shared fork. On a normal fork, any semaphores allocated by the parent before the fork are are not seen are valid semaphores in the child. Any semphores allocated by either process after the fork cannot be seen by the other process.
Answer:
yes. that would be a valid interpretation
Question
are there any restrictions as to what the (int status) can be in the Exit syscall? Is any number a valid one? I'm asking because the child checks the status of the parent to see if it has exited or not, so I'm assuming that there is some status = RUNNING that means the process is running, (that is, we don't call Exit(RUNNING) ).
Answer:
status can be anything. wait only waits on child processes of the calling process, and blocks until it has some information about a dead child process. (otherwise returning error if no child processes exist). so...if it returns without error, the child process _must_ have died. there is no way in this kernel to check the status of a parent
Question
Am I correct in saying that since I have to release the monitor mutex before I block myself in cond_wait, the latest I can do it is after I release the passed-in mutex and before I block myself, making me still vulnerable to lost wakeup, as now that the monitor mutex has been released, another process has a chance to get in and signal before I block myself? If I eventually decide that the best way to do it is by using one of the new syscalls to implement atomic ReleaseMutexAndBlock, will there be any kind of disfavor cast upon our program for using the student syscalls to implement our library? We're already planning to use one, so that we have both an exit that kills all shared forks and an exit that only kills the calling shared fork, to support Thr_exit that only kills one thread and general Exit that will kill the entire program. Is there anything wrong with using the student syscalls for the thread library?
Answer:
There will be no disfavor cast upon your program for this. Just make sure that you implement the whole mutex implementation within the kernel -- otherwise it is a bit of a kludge.
Question
Do we have to prevent deadlock by disallowing Thr_join to by called on tids that are blocked on a join themselves?
Answer:
Nope. Programmers do the funniest things. How do we know they didn't want it to deadlock? Perhaps as an example for an OS class? :-)
Question
What does the departed argument do?
Answer:
If departed is not NULL, it points to a location that is set to the ID of the terminated thread if thr_join() returns successfully.
Question
I noticed that the Q&A mentions a thread library initialization function, but no prototype for some kind of Thr_init is given. Should we put initialization code at the front of Thr_create, with a flag to make sure it only runs once? I think requiring the user to call a Thr_init first is a bit cleaner, but will that break test cases?
Answer:
We really don't have test cases for the threads package -- people will do things very differently. I'd like you to keep everything in Thr_create(), if possible. An init function other than create is a bit of a hack -- not a terrible hack, but definetely ugly.
Question
Are user processes allowed to exit via just returning in main(), or must they call Exit()?
Answer:
You can set a convention on this with respect to your threads package, if you'd like.
Question
just checking, but you'll use the Makefile that we give you to test the kernel with, right? I'm only asking since I made some modification to make the threads library
Answer:
Yes, we will. But, please do document any cool or unusual or different stuff you do, just in caseone of us looses our minds :-)
Question
we implement a copy-on-write fork() and so pages are only allocated when needed during user program execution. However, this means that a program can begin execution and suddenly fail because when it tries to write to a page that needs to be copied first and there isno memory, it seems like the only way to clean up is to kill the process. Is this behavior ok, and if not, can you suggest a different behavior?
Answer:
I think that behavior is fine. I can't really think of anything better.
| Wednesday, October 31, 2001 |
|---|
Question
We're writing the user threads library and have a question about the initialization function. Specifically, we check a global boolean to see if the library's mutex has been initialized. If not we initialize it and some other global data structures. How can we avoid concurrency problems with the allocation of this first semaphore? We're considering using Baker's Algorithm in the initialization function, but are hoping there's a simpler method we've overlooked.
Answer:
In my initialization I specified that the function should be called only by the main thread and only once. I also specified that if this was not done, the behavior of the application was "undefined".
I've also added three syscalls -- these might be helpful to you.
Question
(A). for this project, do we have to worry about ownership of a mutex, that is, only the thread that locks a mutex can release the mutex?
(B). if i'm using semaphore to implement mutex, what happen if a dumb user unlock a mutex twice? can i assume the user never does that?
Answer:
(A). Nope. You don't have to worry about it. But, if you want, you are welcome to "dig in".
(B). Bad user. You broke your program! Ha, ha, ha. Watch us laugh.
Question
Another thing, user can specify what thread Thr_join should wait for. However, Wait() does not have this feature.
Is there a way to solve the above mentioned situation w/o the use of StudentSyscallX ?
Answer:
Using a student syscall is the easiest and most straight-forward way to implement this functionality. However, if you are creative, you can accomplish this in user-space.
Question
Is it okay if we store the total number of clock ticks that have elapsed so far in an int (or unsigned)? This breaks if more than INT_MAX (or UINT_MAX) clock cycles elapse; the length of time this takes depends on how frequent the clock interrupt is, but is about 497 days if there are 100 clock ticks per second. The reason we would do this is that it makes our scheduling faster. If this isn't acceptable, we could use a long long (accepted by gcc and cc, and part of ISO C99, but not supported by older compilers), or make it do a linear scan through all sleeping processes every clock tick.
Answer:
You really shouldn't store a count of clock ticks in any type of data structure or primitive. And, you really shouldn't do a linear scan of the delay queue (a linear insert, perhaps) -- there is a better way!
Question
In going back and reviewing LoadProgram, I've noticed that there will be a block of unallocated pages between the text area of a program, and the data area. This determination is made by the LoadInfo function (provided). I was wondering what the logic of this was, and if I should indeed leave these pages unallocated, since I was under the impression that by definition, every memory address beneath the break had allocated physical space.
Since the break pointer is defined to be the lowest address not part of the program text or the heap, I would assume that it should be placed at the end of the data section of the program. If these pages between text and data are left unallocated, I can't satisfy both conditions. Which one should I go for?
Answer:
This hole in the program is a feature of how the program is linked, and you should leave it as is, it will not hurt anything. Thus the break pointer should go after the data section (otherwise you'de just write in this hole which would be a bad thing).
| Tuesday, October 30, 2001 |
|---|
Question
If a process calls SharedFork, and then one of the two resulting processes calls Exit, should *both* processes exit, or just the one calling Exit?
Answer:
We didn't specify this part of the assignment, so it is completely up to you. We'll accept any reasonable interpretation. But, you might want to give some thought to your threads package. In my solution, I had Exit() kill everything, because it made some things easier for me. But, the important thing is that you do something reasonable and can explain your decision to us.
What are we supposed to do if the process passed to us as init crashes? Halt the kernel, or just let the other processes (if any) keep chugging along? For that matter, is init supposed to be able to wait on child processes?
Answer:
This is a very "bad" situation in a real operating system, but not bad enough to halt the kernel. let the other processes go. if the other processes exit, just let them exit gracefully, and eventually you'll be left with a kernel that's always running idle...
if the user SemDealloc some semaphore that some process is currently waiting for, what should we do to the semaphore and the waiting processes?
Answer:
Something reasonable. One example is to return error. There are many others.
when a user process calls TtyRead(x, y, 10) and the user type abc\n in terminal x, the TtyRead should return 3 with "abc" or 4 with "abc\n"? i'm sure 5 with "abc\n\0" is wrong.
Answer:
It should be 4. The reason for this is to allow the reading process to find out wether the string it read contains an end of line or not. I.e. if it returned 4 and the chars were "abcd" then the user program will know that the buffer it provided was not big enough, and therefore it should call another read to get the rest of the line.
Question
do we have to hide our implementation of Tmutex_t and Tcond_t so the user doenst know what's inside it?
Answer:
You don't need to go through any hoops to "hide it". The user shouldn't need to look inside them.
| Monday, October 29, 2001 |
|---|
Question:
the handout says memory allocation and deallocation should be allowed after shared fork. does that mean Brk() should break for all user processes that share the same address space with the calling process? if that is true, is it a good idea to have a linked list of shared processes in PCB? what if one of the process call Exec after shared fork?
Answer:
In order to properly implement the thread library for the last part of the project, you will want a SharedFork that does move the break for all processes at the same time.As for exec, you may choose semantics as appropriate.
Question:
the Q&A says "If you reparent orphans, you should reparent them directly to init". but the handout says "When the orphan exist, you need not save or report their exit status since there is no longer anybody to care". does that mean we get to choose whether or not we want to reparent an orphan?
Answer:
If you choose to reparent them, it should be directly to init. You should not repparent them to a gradnparent, then a great-grandparent, &c. You are not required to reparent them at all (although we think it is a good idea).
Question:
when a user process call a syscall with an bad address (like TtyRead(3,3,3)), should the syscall return ERROR or kill the calling process? (we kill a process when TRAP_MEMORY with bad uctxt->addr value occurs)
Answer:
For a syscall, but not a trap, the right thing to do is probably to return ERROR. This keeps the kernel safe -- while allowing the app to choose to die or to work around the situation.
Question:
If we run out of memory when mallocing in kernel space, what if the appropriate thing to do? Halt the kernel?
Answer:
The apropriate action is as sane as possible. You should not cause a kernel panic unless there is simply no way to continue. if there is a way to fix up an out of memory (i.e. if you try to fork, and there isn't enough space to malloc a pcb) then just clean up and return a -1 for the return value of the fork.