Lab2: system call
lab2 is still very simple in general. As long as we get through the calling link of system call, there will be no problem. teaching material In Chapter 4, the software and hardware process of xv6 dealing with trap is explained in detail. The specific execution process is not repeated here. Simply explain the execution path of user program execution system call at the function level. Of course, we know that these can complete the experiment.
We take System call tracing as an example to describe the whole process:
First, add $u to the Makefile file/_ Trace, so that trace.c can be correctly compiled and connected to xv6.
The main() function in trace.c completes the purpose of system call tracing by calling the system call function trace(). In order to call trace() correctly, we must declare its definition in user/user.h. System call is used to run through user state and kernel state. This process is controlled by assembly code. Therefore, the implementation of trace () is assembly code, and the assembly code will be directly linked with trace.c in the link stage; In the experiment, the corresponding assembly code is automatically generated through the script. We can see the corresponding template in usys.pl. Here, we only need to add entry("trace"), indicating that we have defined the assembly code implementation of trace (). From the assembly template, we can see that the system call number needs to be passed to a7 before executing the ecall instruction, so we also need to define sys_ System call number of trace; kernel/syscall.h defines the system calls provided by the kernel for user processes. We only need to call SYS_trace set a system call number. The above is all the work required to correctly compile the user state code, but successful compilation does not mean that it can be executed correctly. In xv6, if trace is called, the error of "unknown sys call" will be printed, because we have not implemented the corresponding system call sys in the kernel state_ trace().
SYS_trace connects the user mode system call trace () with the kernel mode system call sys_trace(), after ecall triggers trap, the hardware processing mechanism will SYS_trace is passed to the kernel state, and the instruction flow reaches syscall() in syscall.c. syscall() executes the corresponding system call according to the system call number; To do this, we need to define sys in sysproc.c_ trace() and add this function to syscalls in syscall.c. At this point, a complete system call link is completed.
sys_trace
The next task is to implement sys_trace(), in fact, hits has made it very clear:
Add a field trace for struct proc_ Syscall, used to determine whether the system call needs to be printed in syscall():
// kernel/syscall.c void syscall(void) { int num; struct proc* p = myproc(); num = p->trapframe->a7; if (num > 0 && num < NELEM(syscalls) && syscalls[num]) { p->trapframe->a0 = syscalls[num](); int trace_mode = p->trace_syscall; // To be able to print sys_trace, which must be executed after syscalls[num] is executed if (((1 << num) & trace_mode) != 0) { printf("%d: syscall %s -> %d\n", p->pid, syscalls_name[num], p->trapframe->a0); } } else { printf("%d %s: unknown sys call %d\n", p->pid, p->name, num); p->trapframe->a0 = -1; } }
In addition, in order to print the relevant system call information in the child process, we need to modify fork(), that is, copy the trace of the parent process for the child process_ syscall. The code here is not repeated.
sys_sysinfo
Similarly, Sys is implemented in the kernel_ Before sysinfo(), you also need to complete the process described at the beginning.
Compared to sys_trace(),sys_ The complexity of Sysinfo () lies in how to record the allocation process of free physical memory and the process management process. hits reminds us that we need to look for clues in kernel/kalloc.c and kernel/proc.c.
Observing kernel/kalloc.c, we can find that xv6 allocates and reclaims memory through kmem, so we only need to track kmem.freelist to calculate the size of free physical memory; To do this, we add a global field uint64 freemem = 0;, Execute freemem -= PGSIZE when allocating physical memory, and freemem += PGSIZE when reclaiming physical memory:
// kernel/kalloc.c // the count of free pages uint64 freemem = 0; uint64 getfreemem() { return freemem; } void kfree(void* pa) { struct run* r; if (((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) panic("kfree"); // Fill with junk to catch dangling refs. memset(pa, 1, PGSIZE); r = (struct run*)pa; acquire(&kmem.lock); freemem += PGSIZE; r->next = kmem.freelist; kmem.freelist = r; release(&kmem.lock); } void* kalloc(void) { struct run* r; acquire(&kmem.lock); r = kmem.freelist; if (r) { freemem -= PGSIZE; // update freemem when success kmem.freelist = r->next; } release(&kmem.lock); if (r) memset((char*)r, 5, PGSIZE); // fill with junk return (void*)r; }
Similarly, we can also use a global variable uint64 nproc = 0; Number of surviving processes tracked:
// kernel/proc.c uint64 nproc = 0; // the count of processes is not UNUSED. uint64 getnproc() { return nproc; } static struct proc* allocproc(void) { ... found: p->pid = allocpid(); nproc++; ... } static void freeproc(struct proc* p) { nproc--; ... }
Logical sys_sysinfo() comes out. The last difficulty is how to transfer the sysinfo structure from kernel space to user space. Please refer to kernel/sysfile.c:sys_fstat():
// kernel/sysproc.c int getfreemem(); int getnproc(); uint64 sys_sysinfo(void) { uint64 siAddr; // user pointer to struct sysinfo if (argaddr(0, &siAddr) < 0) return -1; struct proc* p = myproc(); struct sysinfo si; si.freemem = getfreemem(); si.nproc = getnproc(); if (copyout(p->pagetable, siAddr, (char*)&si, sizeof(si)) < 0) return -1; return 0; }
time
Create time.txt in the project root directory and record the time spent completing the lab in it.
Hi, I've finally finished writing. You can learn the following content.