OS161 Flashcards
runprogram
Called by cmd_progthread.
Actually doing the real job of setting up the process space.
Another task is loading the contents of the ELF file corresponding to the program.
In order tasks of runprogram:
- vfs open: Open a file using an abstraction that is machine independent
- create new address space
- Assigne the space to the new process
- Load the elf
- vfs close: close the vnode.
- allocates the third contiguos segment (the stack)
- enter_new_process -> context swtich to newly created process
code:
runprogram(char *progname)
{
struct addrspace *as;
struct vnode *v;
vaddr_t entrypoint, stackptr;
int result;
/* Open the file. */
result = vfs_open(progname, O_RDONLY, 0, &v);
if (result) {
return result;
}
/* We should be a new process. */
KASSERT(proc_getas() == NULL);
/* Create a new address space. */
as = as_create();
if (as == NULL) {
vfs_close(v);
return ENOMEM;
}
/* Switch to it and activate it. */
proc_setas(as);
as_activate();
/* Load the executable. */
result = load_elf(v, &entrypoint);
if (result) {
/* p_addrspace will go away when curproc is destroyed */
vfs_close(v);
return result;
}
/* Done with the file now. */
vfs_close(v);
/* Define the user stack in the address space */
result = as_define_stack(as, &stackptr);
if (result) {
/* p_addrspace will go away when curproc is destroyed */
return result;
}
/* Warp to user mode. */
enter_new_process(0 /*argc*/, NULL /*userspace addr of argv*/,
NULL /*userspace addr of environment*/,
stackptr, entrypoint);
/* enter_new_process does not return. */
panic(“enter_new_process returned\n”);
return EINVAL;
}
vnode structure
Top level file control block: reference counte, spinlo ck to access the count, pointer to filesystem that will contain a pointer to data and operation on the file systems.
IN VN_FS we can have emulated filesystems (fs of host machine), or simple filesystem (sfs).
How do we read from a file?
struct iovec iov;
struct uio ku;
struct addrspace *as;
Elf_Phdr ph; /* “Program header” = segment header */ off_t offset = …;
uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);
result = VOP_READ(v, &ku);
ELF files
ELF files contain address space segment descriptions, which are useful to the kernel when it is loading a new process.
Each ELF segment describes a contiguos region of the virtual address space.
For each segment, the ELF file has a segment image and a segment header, which describe:
- the virtual address of the start of the segment
- the length of the segment in the virtual addres space
- the location of the start of the image in the elf file
- the length of the image in the ELF file
Why is size replicated twice? The image is the exect copy of the binary, that may be smaller than the segment size, leaving the last parts of the segment zero-filled.
In OS161:
- dumbvm implementation assumes that an ELF file contains two segments:
- text, containing the program code and any read-only data
- data segment, containing any other glabal program data
- the elf file does not describe the stack, the stack has a fixed size
- dumbvm creates a stack segment for each process, 12 pages long located at the end of the process address space.
Trap frame fields in syscall
- tf_v0:
- callno (syscall number)
- the return value assigned to it at the end
- tf_a0, tf_a1, tf_a2: syscall parameters (need to be caster
- tf_a3: 0 if no errors occurred, 1 otherwise
- tf_epc: program counter
How is syscall called?
tf_v0 is assigned a value by the assebly code:
- then an exception is generated, that is handled by assembly level exception handler
- that then calls mips_trap that checks if the generated trap has code corresponding to the one defined for syscalls
- if so calls syscall passing to it tf
tf_v0 is then used in a switch to select which syscall to run.
Multi-core system on OS161 why it wouldn’t work as a mutual exclusion mechanism to disable interrupts?
- Because disabling interrputs on the currently used CPU wouldn’t stop other threads and other CPU.
- Temporarily disabling interrupts on all the CPU wouldn’t work either, because other threads could be already running, potentially competing for shared resources
- Disabling interrupts works only on single core platforms, where the number of running threads at a given time is just one.
Spinlock in semaphore P, V
Why is the while used in P?
- P: wait
- V: signal
Spinlock is used to protect counter and because waiting channels need it to work.
P waits the counter to be != 0, wchan_sleep doens’t guaranteed that the condition will be true at the return from wchan_sleep, even if it was true when wchan_wakeone was called the condition was indeed true.
Multiple threads could be working on that same counter.
Threads are not guaranteed to be woken up in a FIFO fashion.
wchan_sleep, wchan_wakeone
why spinlock?
wakeone wakes up more than one thread?
- wchan sleep:
- needs to release the spinlock before calling thread_switch
- will acquire lock again after the thread has been brought back in execution
- wchan_wakeone:
- doesn’t need the lock, in os161 we just use it to check that the spinlock owner is the same that colled the the function
- wakeone wakes up only one thread, in removes just the head from the linked list of waiting threads
- wakeall puts all the threads in the ready queue in execution again
two versionso of sys_exit
v2:
sys_exit(int status) {
struct proc *p = curproc;
p->p_status = status; // save exit status in process
proc_remthread(p->curthread); // detach thread from process
lock_acquire(p->p_lock);
cv_signal(p->p_cv, p->p_lock); // signal termination of thread to sys_waitpid that is wating on the condition variable
lock_release(p->p_lock);
// no as_destroy -> proc_destroy will call it.
thread_exit(); // thread ends
}
v1:
sys_exit(status) {
struct addrspace *as = proc_getas();
as_destroy(as);
thread_exit();
panic(“thread_exit returned (should not happen)\n”);
(void) status;
}
argv argc
- It is necessary to copy argv, argc parameters to user-memory because they are originally in kernel.
- The copy should be created in user memory, more specifically in the beginning (high address) of the stack.
- The origanl values are in kernel memory, not accessible to user programs, but even if the user process could access kernel memory a copy should me made anyway:
- the thread could modify those value, thus to guarantee consistency we need to duplicate them.
copyin, copyout
They are used to copy data between user and kernel memory.
Copyin has as destination the kernel mem, and copyout has as destination the user memory.
Address space for the kernel
There are two possibilities:
- kernel in physical space -> disable address translation for kernel
- kernel in separate virtual address space -> need a way to change address
- translation when moving between privileged and unprivileged code
- OS161, Linux use third approach:
- the kernel is mapped into a portion of the virtual address space of every process
- memory protection mechanism is used to isolate kernel
- advantage: application virtual addresses are easy to use for the kernel
Address translation
Dumb does this:
MIPS MMU tries to translate each virtual address using MMU entries
if page fault (address exception) ->
vm fault function handles exception: uses information from current proces addrspace to construct and load a TLB entry for the page.
On return from exception the MIPS retries the instruction that generated fault.
Program loading in addrspace
An address space must be created for each new process, and load the program code inside the address space.
Program’s code and data are described in ELF (Executable and Linking Format), that is generated after compilation/linking.
the parameter of execv/execvp is the name of the ELF file.