![]() |
|
||
![]() |
| Q22. In LoadProgram, the following error is reported: >>>> The Yalnix kernel (not a user program) has >>>> accessed a bad address in region 1 (0x00100000). >>>> This probably occurred because your page table >>>> for this address is not set correctly. >>>> We will now stop pretending to be real hardware >>>> and try to generate a core dump of your kernel. >>>> It may be useful to look at this core file with a debugger I have followed the instructions in load.template, what shall I do? |
Answer:
Check your load.c to make sure:
|
| Q21.We are not sure where the arg[] for a program come from since if there are two parameters after ./yalnix, how could the system differentiate the second one as a new program or only the parameter for the first program. |
| Answer: We can assume each parameters is a new program to be loaded and run. For example, if we have the command line 'yalnix init1 init2', it means we need to call LoadProgram twice, load both init1 and init2 in. |
| Q20.
Do we have to assume that all the user programs (like idle, init, init1,
init2) we run have Pause() (or Exit() or Delay() )in them at which it will
wait for the next interrupt.
Can't there be any user program which just runs in an infinite loop
printing messages like while(1) { printf("init\n"); } |
| Answer: Yes, we can have such programs. But this will make it difficult to observe some other behaviors of Yalnix, such as context switch. Besides,a timer interrupt will also trigger a context switch if there is another runnable process.So you cannot assume that processes will always Pause() or Exit(). Of course, if a process goes into an infinite loop like the one you suggested, the kernel won't get a chance to close down cleanly until it or the process are manually killed. |
| Q19. Do we need to do context switch when TRAP_TTY_TRANSMIT or TRAP_TTY_RECEIVE happens? |
Answer: It depends on your design. There is no golden rule for this.
When TRAP_TTY_RECEIVE happens, What we need to do is as follows:
|
| Q18. Could you give us a checklist for context switch? |
Answer: When the process run for the first time:
|
| Q17. I have a question regarding the "code" value in the User context structure in the case of TRAP_MEMORY . The yalnix handout says that the code value would indicate the type of disallowed memory access. Where do we find these codes defined? |
| Answer: As far as I know, yalnix does not give out the illegal memory access code. However, we can infer the type of disallowed memory acccess by yourself in memory trap handler. The primary illegal memory accesses are: (1) out of free physical memory. there are not enought free memory frames. (2) out of virtual memory, the address is out of bound. |
| Questions asked before Oct. 25th |
| Q16. I read in the handout that all system calls enter the kernel "through the trap_lernel trap". I am not quite sure what this means. Is TRAP_KERNEL supposed to know which system call is being executed and call it somehow? |
| Answer:
The kernel trap handler know all system call that yalnix can handle,
the code can be as follows: void Kernel_Trap_Handler(UserContext * context) // the kernel trap handler { switch(context->code) // here we know which system call is invoked { case YALNIX_FORK: ..... break; case YALNIX_EXEC: .... break; ... } |
| Q15. Is there a list of system call numbers somewhere? Is the list in the handout in order starting a 0? |
| Answer: The system call code is defined in yalnix.h, wich is in the same directory as hardware.h. |
| Q14.
I am looking at the load.template and says:
==>> Change the protection on the "li.t_npg" pages starting at ==>> virtual address VMEM_1_BASE + (text_pg1 << PAGESHIFT). Note ==>> that these pages will have indices starting at text_pg1 in ==>> the page table for region 1. Does this mean that text_pg1 by itself could reference something in region zero? In other words, is it necessary to use it as an offset into region 1 as it is above? This brings me to another question... is it wrong to have one page table for both regions? Is region 1 page table supposed to start at index 0? I hope my questions make sense. I would think that since every process has a view of the entire address space, VMEM_1_BASE would be equal to text_pg1. Please correct me if I'm wrong. |
| Answer: Yes, li.t_npg should be used as an offset for region 1. We can maintain one page table for each process. However, to improve efficiency, we can maintain two page tables, one for Region 0, the kernel region. Another for region 1, the user region. Thus when a context switch happens, we will just reload the page table that related to user region, and do not need to reload the kernel region as all processes share the same kernel space. |
| Q13. If a user types in a line of length > TERMINAL_MAX_LINE, what will the TtyReceive() hardware interface do? Will it copy the whole line to the buffer and return length? Or will it ignore the characters beyond TERMINAL_MAX_LINE and copy only TERMINAL_MAX_LINE characters to buffer? |
| Answer: If we set the third parameter, len, as TERMINAL_MAX_LINE, TtyReceive will return the actual length of the input. For example, the user type 1000 characters and hit Enter, TtyReceive will return 1000 in this case. However, if the length is not set to TERMIANL_MAX_LINE, the behavior will be a little different. Let's assume we call TtyRecieve in a form TtyReceive(tty_id, buf, MY_LEN), MY_LEN <> TERMIANL_MAX_LINE. In this case, if a user input a line of length > MY_LEN, then TtyReceive will just return the first MY_LEN characters. We need to call TtyRecieve for sereral times to get all input. |
| Questions asked before Oct. 22nd |
| Q12. When I read about the free list the first time I thought it would be a linked list of free pages, but it also seems that it is impossible to do before we have the ability to dynamically allocate memory with malloc. Which one is correct? |
| Answer: Since OS deal with the physical memory, and memory has already been there, all that we need to do is to keep a record of which frame has been used, and which frame has not been used. So we need only to keep track of the page # and do not need to dynamically allocate/deallocate memory. |
| Q11. when I try to use &__data_start I get an error that says it is undefined. Where is it defined? |
| Answer: __data_start is a char pointer that is defined internally in yalnix. So have a external declartion 'extern char * __data_start' in your code, it will be fine. &__data_start will give out the starting address of kernel data segment. |
| Q10. How can I allocate some memory for kernel page table? Can we use malloc in KernelStart? |
| Answer: Yes, we can use malloc in KernelStart() to allocate space for free page list. However, it must be used before virtual memory is enabled. |
| Q9. I really want to know that (1) what kind of information are there in the kernel stack? (2) Why should these information be stored in the kernel stack? (3) how does these information work when context switch occurs? |
| Answer:
The kernel stack is just a stack of function arguments,
local variables, and return addresses.
In case a process is running in the kernel space by invoking a system
call, and an interrupt happens at the same time, this process will be
suspended. However, it is improper to put the kernel-related information,
such as the local variables used in system call in the user space. This is
the reason that kernel stack is needed. In real operationg systems, there are some processes/threads running in kernel address space. These processes/threads share the same address space, and each is allocated its own stack at the time it is created. It is obvious that it will be difficult to sapce these stakcs in a fixed address space in such a way that there is room for growth beneath every stack while still leaving much room for the kenel to store other information. However, in most cases, we can assume that the kernel stack will not exceed a fixed amout of memory. Each process will maintain a memory page list for its kernel stack. This information can be put in PCB or someplace that is accessible to the process. When the context switch occurs, the kernel stack pages for the new process will be filled into the kernel's page table. |
| Last year questions and questions asked before Oct. 8 |
| Q8. Should idle process have a PCB, user virtual region page tables, kernel context,user context and kernel stacks? Because the thing is that idle process is always in kernel memory. So "why do we need to" and "how can we" allocate page tables, kernel stacks,etc. for it. Also when we do a kernel context switch to idle process, do we need to copy all the user context and kernel context to the idle process PCB, if we are maintaining a PCB. |
| Answer: Idle process is similar to other process, except that its virtual region 1 mapping is all invalid. So you have to initialize Idle process just like all other processes. As for KernelContext, you need to copy usercontext and kernelcontext just like you do for other processes for idle process also. |
| Q7. If we run a TtyWrite from init1 process. It gets blocked and then if now we switch over to init2, then it inherit kernel context and kernel stack of the blocked proces, which i think is not correct. Should we do it this way or some other way to get init2 for the first time |
| Answer: There is no problem with this as blocked process is maintained in your global queues, not in kernelContext/Kernel Stack. |
| Q6. We need a Queue of lines being read in, so if I enter 3 lines to terminal 1 and then a user process reads the lines in they should be read in FIFO order, right? Or can I just copy over the old buffer if no one has read it, when I recieve a new one? |
| Answer: Yes, the input should be read in FIFO order. If the string is longer than TERMINAL_MAX_LINE, you should split it into buffers of maximum TERMIAL_MAX_LINE and call TtyTransmit multiple times. |
| Q5.
We are confused about the way we should map our kernel stacks. When
we call sbrk(0), that returns the lowest address not in use by the
kernel, and that includes the initial stack that is in use when
KernelStart() gets called, right?
Now, our real question is how do we figure out where in physical memory
this initial stack is? Is it at the top of the used space, i.e. just
below sbrk(0)? But if we are already executing in that kernel stack using addresses below sbrk(0) in physical space, what should we do, should we just leave the original stack alone, by keeping its virtual-to-physical mapping as the identity map, but then when we create the idle or init process, abandon the original stack and never use it again so that all subsequent kernel stacks have the correct virtual addresses? |
| Answer: sbrk(0) returns the lowest address that is not used by the kernel. But this block of memory does not include the initial stack. Since each process will have its own kernel stack pages, you need not to worry about the kernal stack page mapping when KernelStart() is called. |
| Q4. Is &__data_start supposed to be on a page boundary? Ours isn't, so we had to map the whole page it was on to the data segment. |
| Answer: &__data_start can be inside a page also. But hardware ensures that code segment and data segment does not overlap. So you can safely use (&__data_start >> PAGESHIFT) as your first data segment page. |
| Q3.
I am still confused about these break addresses. According to definition,
they are the lowest addresses that are not in data sections. So the highest
virtual page number of data section is: UP_TO_PAGE(addr)>>PAGESHIFT (when break is not in page boundary) or (UP_TO_PAGE(addr)>>PAGESHIFT ) - 1 (when break is in page boundary) Which one is always correct? |
| Answer:
After virual memory initialization, deal with page numbers instead of
addresees to keep track of the "top of heap" in Kernel and User Process.
It's much simpler that way. Coming to virtual memory initialization :
sbrk(0) gives the lowest address not in heap. Two Cases: 1. addr = sbrk(0) lies on page boundary: so addr>>PAGESHIFT gives a page which is not in heap. So highest page number of heap is ( addr>>PAGESHIFT ) - 1. Also in this case, addr>>PAGESHIFT -1 == (UP_TO_PAGE(addr)>> PAGESHIFT) -1 (as addr lies on Page boundary) 2. addr = sbrk(0) is inside a page: so addr>>PAGESHIFT gives a page which is in heap (i.e highest page number of the heap). Also, in this case, addr>>PAGESHIFT == ( UP_TO_PAGE(addr)>> PAGESHIFT ) - 1 |
| Q2. Can you tell me the interval at which the clock trap is generated? I think it is definitely shorter than 1 second as suggested by the handouts (if I remember correctly). |
| Answer: Actual interval is dependent on all processes that are running on your computer as it's simulated in hardware. Interval of clock trap does not effect the correctness of your kernel. |
| Q1.
We are trying to do context switching from the idle process
to the first init process. In order to do that, we save the old user
context, then load the new user context, also we copy the kernel stack of
the old process, to the kernel stack of the new process, and when we call
the kernelContextSwitch function, it did not seem to do the work
correctly, and we are given the error messgae as below: ABORT: KernelContextSwitch: Kernel function returned bogus context. We are just wondering how to call the kernelContextSwitch function correctly, in other words how can the kernelContextSwitch find and pass the kernelContext of the old process to MyKCS. When we are trying to work around this problem, instead of calling kernelContexSwitch function, we only call MyKCS, so we can switch from idle process to the first user init process, but we can never switch back to the idle process.Can you please give us some more instructions? |
| Answer: It's happening because you have not initialized the kernel context and stack of the process you are switching to.So first time you run idle/init2, inside MyKCS you would like to initialize kernel context. Kernel context is copied from running process. As for kernel stack, we need to point the corresponding page table entries to new phyiscal pages. |
School of Computing • 50 S. Central Campus Dr. Rm.
3190 • Salt Lake City, UT 84112
801-581-8224 • Send comments to yuyang@cs.utah.edu
Disclaimer