Lab - System Calls
Lab - System Calls
Before you begin lab system calls, read Chapter 2 of the xv6 book, and Sections 4.3 and
4.4 of Chapter 4, and related source files:
The user-space "stubs" that route system calls into the kernel are in user/usys.S,
which is generated by user/usys.pl when you run make. Declarations are in
user/user.h
The kernel-space code that routes a system call to the kernel function that
implements it is in kernel/syscall.c and kernel/syscall.h.
Process-related code is kernel/proc.h and kernel/proc.c.
In this lab, there are several questions for you to answer. Questions are in boxes with a
light orange background. Write each question and its answer in your notebook. Take
photo(s) of your questions/answers and submit the photo(s) on Canvas.
The Linux grep command can be helpful on C programming using Linux with a terminal
and on some questions. For example, suppose a question asks you about the struct proc.
You can discover the definition and uses of the struct proc by issuing the following
Linux grep command in the kernel directory.
$ grep syscall *.c
syscall.c:#include "syscall.h"
syscall.c:// An array mapping syscall numbers from syscall.h
syscall.c:static uint64 (*syscalls[])(void) = {
syscall.c:syscall(void)
syscall.c: if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
syscall.c: p->trapframe->a0 = syscalls[num]();
trap.c: syscall();
trap.c: // send syscalls, interrupts, and exceptions to uservec in trampoline.S
% grep syscall *.h
defs.h:// syscall.c
defs.h:void syscall();
In the directory of your xv6-labs, create two files: answers-syscall.txt and time.txt that I
may use if I run your code using your zip file submission. The answers-syscall.txt is a
blank file. The time.txt file contains the number of hours you spent on the lab.
$ echo > answers-syscall.txt
$ echo 8 > time.txt
Xv6 has a small collection of system calls, which are used to create utility programs. Using the system calls, you
can create programs that open, close, and read files; create and delete files and directories; create processes;
communicate between processes using pipes, read, and write; and execute programs. The API for Xv6 system
calls are shown in yellow in the following table. In this lab you create the Xv6 system calls shown in red.
https://gusty.bike/labs/syscall.html 1/17
00:10 7/1/25 Lab: System calls
int exit(int Terminate the current process; status reported to wait(). No return. Wait for a child to exit; exit
status) status in *status; returns child PID. Terminate process PID. Returns 0, or -1 for error.
int wait(int Wait for a child to exit; exit status in *status; returns child PID.
*status)
int exec(char Load a file and execute it with arguments; only returns if error.
*file, char
*argv[])
char *sbrk(int n) Grow process's memory by n bytes. Returns start of new memory. The C runtime function
malloc() calls sbrk() to get space for the heap.
int open(char Open a file; flags indicate read/write; returns an fd (file descriptor).
*file, int flags)
int write(int fd, Write n bytes from buf to file descriptor fd; returns n.
char *buf, int n)
int read(int fd, Read n bytes into buf; returns number read; or 0 if end of file. Release open file fd.
char *buf, int n)
int dup(int fd) Return a new file descriptor referring to the same file as fd.
int pipe(int p[]) Create a pipe, put read/write file descriptors in p[0] and p[1]. Change the current directory.
int fstat(int fd, Place info about an open file into *st.
struct stat *st)
int link(char Create another name (file2) for the file file1.
*file1, char
*file2)
https://gusty.bike/labs/syscall.html 2/17
00:10 7/1/25 Lab: System calls
int trace(int Enables tracing of Xv6 system calls which numbers match bits in mask.
mask)
int Return system information - number of free bytes and number of procs.
sysinfo(struct
sysinfo *info)
kernel/syscall.c: Add dosys information to kernel/syscall.c. syscall.c has the fucntion syscall that is
called from trap. The function syscall determines which system call has been called, retrieves arguments,
and makes the call. syscall.c has function prototypes for each system call and an array (syscalls[]) of
function pointers that point to the various system calls. The array of function pointers uses the SYS_dosys
number to place the address of the sys_dosys function in the array. The system call number is placed in
register a7 by usys.S prior to calling the function syscall. You can examine the code in syscall.c to see
the following statements to call the correct system call.
user/user.h: Add prototype for the dosys system call to user/user.h The file user.h provides a seqence of
function prototypes that serve as the API for system calls. User utility programs like ls and cat include
user.h to access the API. The file user.h also has function prototypes for functions that are in ulib.c. The
ulib.c functions can be called directly, and the include functions such as strcpy and printf.
user/usys.pl: Add a stub to user/usys.pl The Makefile invokes the perl script user/usys.pl, which
produces user/usys.S, the actual system call stubs, which use the RISC-V ecall instruction to transition
from user mode to the kernel mode. If you examine the code in usys.pl, you will discover the system call
number is placed in register a7 prior to executing the ecall instruction.
dosys Implementation: kernel/sysproc.c is the entry point system calls. For some system calls, the entire
implementation is in sysproc.c. For example, you will place your implementation of rinter in the
function sys_rinter() in sysproc.c. However, other system calls such as fork() the implementation is
located in kernel/proc.c, and the sys_fork() function simply calls fork().
Testing/Makefile: You will have to create a user program to test your syscall implementation. Add
$U/_dosystest to UPROGS in Makefile. To test your dosys system call, create your test cases program in
user/dosystest.c. Then add $U/_dosystest to UPROGS in Makefile.
https://gusty.bike/labs/syscall.html 3/17
00:10 7/1/25 Lab: System calls
A system call is different from a regular function call. A system call must transfer control from the user mode
process to the kernel mode OS. On the RISC-V architecture, this is accomplished with the ecall assembly
instruction. The ecall instruction generates a trap that must be handled by the OS. The following describes the
sequence of events for the rseed() system call. The sequence is the same for all system calls. Some system calls
have more or less arguments.
There are some ideas in this explanation of events that will require you to study virtual addresses and page
tables, which is immediately after our system call study.
A user program is running as a process in user mode. The process calls rseed(555).
The function calling conventions of RISC-V places up to 8 arguments in registers a0, a1, a2, a3, a4, a5, a6,
and a7.
In calling rseed(555) places 555 in register a0, and generates a regular function call to rseed.
jalr rseed
When you add the stub entry("rseed"); to user/usys.pl and the macro #define SYS_rseed 24 to
kernel/syscall.h you are adding the user level function rseed() and the system call number to Xv6.
The stubs in user/usys.pl are processed by Perl to generate the file usys.S, which is a collection of
assembly functions, one for each system call. In this example, the assembly function for rseed() looks like
the following.
rseed:
li a7, SYS_rseed
ecall
ret
The code in usys.S is linked into every user program, which allows each user program to call system calls.
Notice that the assembly functions in usys.S are called in user mode. The ecall instruction transitions to
kernel mode.
At the point of the ecall instruction in this example, the value 555 is in register r0 and the value SYS_rseed
is in register r7, which as defined for this example is 24.
The ecall instruction generates a trap that changes the CPU mode from user to kernel. This trap must be
handled by an OS trap handler.
During initialization, the function main in main.c calls functions in trap.c to install the kernel trap vector
(or address) in the stvec register. The function kernelvec.
handles interrupts when the CPU is in kernel mode.
When the CPU is in user mode, the stvec register contains the address of the Trampoline, which is the user
mode interrupt handler. The Trampoline code is in the file kernel/trampoline.S.
The Trampoline code fits on one page, and there is only one copy of the Trampoline code in memory.
Each process has the Trampoline mapped to the exact same address, which is placed in the stvec register.
In addition to the Trampoline page, each process is allocated a trapframe page, which is used to store is
registers when a trap occurs. Each process has the trapframe mapped to the same page.
When the ecall instruction generates the trap, it is processed by the kernel on the Trampoline page. At this
point, the user pagetable is being used, but we are running in kernel mode. The user's Trampoline page is
marked such that it can execute in kernel mode. The Trampoline code saves the user's registers on the
user's trapframe, switches to the kernel's page table, and calls usertrap() located in trap.c.
usertrap() recoginizes this is a system call and calls the function syscall() located in the file syscall.c.
If you examine syscall, you will see it retrieves the value of a7 from the trapframe. The user mode
function rseed places the system call number in a7, which was saved on the trapframe by the Trampoline
The register a7 has the system call number, which syscall uses to call the correct system call function. In
this case, it results in a call to sys_rseed().
Notice the sys_rseed() does not have arguments, but rseed(555) has arguments.
sys_rseed() retrieves its argument from register a0 by calling argint, which is located in syscall.c.
argint retrieves the register's value from the trapframe, which is where the process's registers were saved
when the trap occurred.
https://gusty.bike/labs/syscall.html 4/17
00:10 7/1/25 Lab: System calls
The CPU is now executing the kernel's code corresponding the system call.
When the system call is complete, the code unwinds, eventually doing a return from trap which goes to the
reg instruction shown above the the rseed() assembly function.
To learn more about how to run GDB and the common issues that can arise when using GDB, check out the
following.
GDB Sample Sessions - definitely read this before starting the lab.
GDB Reference Card
GDB Webpages TOC
GDB Webpages Index
To help you become familiar with gdb, run make qemu-gdb and then fire up gdb in another window (see GDB
Sample Sessions). Once you have two windows open, type in the gdb window:
(gdb) b syscall
Breakpoint 1 at 0x80002142: file kernel/syscall.c, line 243.
(gdb) c
Continuing.
[Switching to Thread 1.2]
The layout command splits the window in two, showing where gdb is in the source code. The backtrace prints
out the stack backtrace.
Type n a few times to step pass struct proc *p = myproc(); Once past this statement, type p /x *p, which
prints the current process's proc struct (see kernel/proc.h>) in hex.
You will notice that a proc struct has a member trapframe which contains the proc's registers when a trap is
processed. The value that p /x *p prints is the address of the trapframe member. To view the contents of
trapframe, you must enter p /x *p->trapframe. This will show you the hex values of the registers saved on the
trapframe. Alternatively, you can view the value of register a7, by entering p /x (*p->trapframe)->a7. Examine
the use of parentheses in the command to disply the value of the register a7 in the trapframe structure. You may
want to try entering p /x *p->trapframe->a7 to help you better understand dereferencing C pointers.
https://gusty.bike/labs/syscall.html 5/17
00:10 7/1/25 Lab: System calls
2. What is the value of p->trapframe->a7 and what does that value represent? (Hint: look
user/initcode.S, the first user program xv6 starts.)
The processor is running in kernel mode, and we can print privileged registers such as sstatus (see RISC-V
privileged instructions for a description):
(gdb) p /x $sstatus
3. What was the previous mode that the CPU was in?
In the subsequent part of this lab (or in following labs), it may happen that you make a programming error that
causes the xv6 kernel to panic. For example, replace the statement num = p->trapframe->a7; with num = * (int
*) 0; at the beginning of syscall, run make qemu, and you will see:
hart 2 starting
hart 1 starting
scause 0x000000000000000d
sepc=0x000000008000207e stval=0x0000000000000000
panic: kerneltrap
The privileged register scause contains the cause of the trap. When a trap is taken into S-mode, scause is written
with a code indicating the event that caused the trap. Lookup what the value of 0xd (13) in scause means (see
RISC-V privileged instructions for a description).
To track down the source of a kernel page-fault panic, search for the sepc value printed for the panic you just
saw in the file kernel/kernel.asm, which contains the assembly for the compiled kernel.
4. Write down the assembly instruction the kernel is panicing at. Which register
corresponds to the varialable num?
To inspect the state of the processor and the kernel at the faulting instruction, fire up gdb, and set a breakpoint at
the faulting epc, like this. Note, be sure the use the address you saw displayed in the panic: kerneltrap.
(gdb) b *0x000000008000207e
Breakpoint 1 at 0x8000207e: file kernel/syscall.c, line 247.
(gdb) layout asm
(gdb) c
Continuing.
[Switching to Thread 1.3]
5. Confirm that the faulting assembly instruction is the same as the one you found above.
https://gusty.bike/labs/syscall.html 6/17
00:10 7/1/25 Lab: System calls
6. Why does the kernel crash? Hint: look at figure 3-3 in the text; is address 0 mapped in
the kernel address space? What is the value in scause above? Does this value confirn why
the kernel crashed. (See description of scause in RISC-V privileged instructions)
Note that scause was printed by the kernel panic above, but often you need to look at additional info to track
down the problem that caused the panic. For example, to find out which user process was running when the
kernel paniced, you can print out the process's name:
(gdb) p p->name
7. What is the name of the binary that was running when the kernel paniced? What is its
process id (pid)?
This section and the links at the top of this section contain helpful information on gdb.
In this problem you will add system calls rseed and rinter that seed a random integer
generator and generate random integers. This problem introduces you to the Xv6 system
call mechanism. You'll create a new rseed system call that will seed the random integer
generator. It shall take one argument, an integer "seed", that establishes the starting seed
for the Pseudo Random Number Generator (PRNG). You'll create a new rinter system
call that will return an pseudo random integer. It shall take one argument, an integer max
such that the random number returned is between 0 and max minus one. Both rseed and
rinter shall return -1 on an error.
You shall create a static variable to contain the rseedv, which shall be 0 by default.
Notice the expression for MY_RAND_MAX results in 0x7fffffff, which is the largest positive 32-bit number.
The C bitwise and operator is a single &. When you use a bitwise and in the expression
(1103515245 * rseedv + 12345) & MY_RAND_MAX
Some Hints
Add $U/_randomtest to UPROGS in Makefile
Run make qemu and you will see that the compiler cannot compile user/randomtest.c, because the user-
space stubs for the system calls don't exist yet:
The Makefile invokes the perl script user/usys.pl, which produces user/usys.S, the actual system call
stubs, which use the RISC-V ecall instruction to transition to the kernel.
Once you fix the compilation issues such that make qemu successfully makes, you can run randomtest 555;
but it will fail because you haven't implemented the system calls in the kernel yet.
Add a sys_rseed() function in kernel/sysproc.c that implements the new system call by assigning its
argument to the static variable rseedv.
Add a sys_rinter() function in kernel/sysproc.c that implements the new system call by using the Linear
Congruential General algorithm defined above.
https://gusty.bike/labs/syscall.html 8/17
00:10 7/1/25 Lab: System calls
Modify the syscall() function in kernel/syscall.c to include the information needed to make the system
calls. Follow the pattern of exising system calls.
Once you updated kernel/sysproc.c and kernel/syscall.c, perform a make qemu, and run $ randomtest
555 as a utility program in Xv6 to see if you have implemented it correctly. Use the sample outputs
provided above for comparisons.
Testing
We provide a randomtest user-level program that calls your rseed and rinter system calls (see
user/randomtest.c). When you're done, you should see output like this:
$ randomtest 555
40
69
74
3
60
1
10
39
96
49
$ randomtest 101
42
79
84
53
94
79
44
9
10
19
$ randomtest 43
24
33
62
63
88
1
26
59
52
89
8. The system calls for random numbers were a nice introduction to system calls. You
learned how to add OS system calls to the Xv6 OS. Do they have to be system calls?
Examine the files kernel/string.c and user/umalloc.c and how they fit into Xv6 before
answering this question.
In this problem you will add a system call tracing feature that may help you when
debugging later labs. You'll create a new trace system call that will control tracing. It
should take one argument, an integer "mask", whose bits specify which system calls to
https://gusty.bike/labs/syscall.html 9/17
00:10 7/1/25 Lab: System calls
trace. For example, to trace the fork system call, a program calls trace(1 << SYS_fork),
where SYS_fork is a syscall number from kernel/syscall.h. You have to modify the xv6
kernel to print out a line when each system call is about to return, if the system call's
number is set in the mask. The line should contain the process id, the name of the system
call and the return value; you don't need to print the system call arguments. The trace
system call should enable tracing for the process that calls it and any children that it
subsequently forks, but should not affect other processes.
Let's first consider the int trace(int) function prototype that is added to user.h. The return code is 0 for
success and -1 for failure. The argument is an integer, which is a 32-bit quantity. Each bit corresponds to a
system call number, which is defined in syscall.h. For example, bit 1 specifies to trace fork system calls
because fork has number 1 as its system call number. This means if you make a call trace(2), you are tracing
fork system calls because 2 has bit 1 set. Another example is trace(0x20) traces read system calls because read
has number 5 as its system call number and 0x20 has bit 5 set. The C expression 1<<5 results in the number
0x20, because 1 is shifted 5 bits to the left. If you want to trace both fork and read system calls, you would call
trace(0x22) which sets bits 1 and 5.
When calling the trace user program from the command line, the examples show the first parameter as in
decimal integer. The samples provided in the Testing section discuss translating this decimal integar parameter to
specific system calls.
Some Hints
Many of these hints are similar (but maybe briefer) to the hints for the system calls for random numbers.
Add $U/_trace to UPROGS in Makefile. The file user/trace.c contains the test cases for the trace system
call.
You could run make qemu and you will see that the compiler cannot compile user/trace.c, because the
user-space stubs for the system call don't exist yet.
Add a stub to user/usys.pl, and a syscall number to kernel/syscall.h. The Makefile invokes the perl
script user/usys.pl, which produces user/usys.S, the actual system call stubs, which use the RISC-V
ecall instruction to transition to the kernel.
Once you fix the compilation issues, you could run trace 32 grep hello README; but it will fail because
you haven't implemented the system call in the kernel yet.
Update the kernel/proc.h file by adding a new member int mask to the struct proc structure.
The following is the code for the updated struct proc structure in kernel/proc.h. You are only adding one
line.
struct proc {
struct spinlock lock;
Add a sys_trace() function in kernel/sysproc.c that implements the new system call by remembering its
argument in a new variable in the proc structure (see kernel/proc.h). The functions to retrieve system call
arguments from user space are in kernel/syscall.c, and you can see examples of their use in
kernel/sysproc.c. This programming is just like the random number system calls, which contain
additional information on this step.
uint64
sys_trace(void)
{
int n;
argint(0, &n);
myproc()->mask = n;
return 0;
}
Modify fork() (see kernel/proc.c) to copy the trace mask from the parent to the child process. fork()
creates a child process, which is identical to its parent. Both the parent and child have struc proc that
define the process attributes. The parent has a trace mask stored in its struct proc, which is what you
added in the previous step. You must copy this trace mask to the child.
The following is the code of a modified fork(), which is in kerenl/proc.c. You are only adding one line.
int
fork(void)
{
int i, pid;
struct proc *np;
struct proc *p = myproc();
// Allocate process.
if((np = allocproc()) == 0){
return -1;
}
https://gusty.bike/labs/syscall.html 11/17
00:10 7/1/25 Lab: System calls
if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
freeproc(np);
release(&np->lock);
return -1;
}
np->sz = p->sz;
pid = np->pid;
release(&np->lock);
acquire(&wait_lock);
np->parent = p;
release(&wait_lock);
acquire(&np->lock);
np->state = RUNNABLE;
release(&np->lock);
return pid;
}
Modify the syscall() function in kernel/syscall.c to print the trace output. You will need to add an array
of syscall names to index into. Make sure that the indices of the array use the SYS_fork names defined in
syscall.h. For example
If you study the code in syscall(), I am sure you can find the variables that correspond to pid,
syscallName, and syscallReturnValue. These variables may be members of structures.
The following is the code for the syscall() modifications. This does not show the entire code for
syscall.c. Just enough to show the modifications, which are in green font. Be sure that the order of your
syscall_name[] matches the order of your system call numbers.
void
syscall(void)
{
int num;
struct proc *p = myproc();
//num = * (int *) 0;
num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
// Use num to lookup the system call function for num, call it,
// and store its return value in p->trapframe->a0
p->trapframe->a0 = syscalls[num]();
Testing
We provide a trace user-level program that runs another program with tracing enabled (see user/trace.c). Do
not simply run the trace program. Study the code in user/trace.c. When the trace program is run, it is a child
process of the shell. You will see that the trace user program calls the trace() system call with the mask
provided. This sets the mask member of the struct proc to be the mask. This is for the trace process. The trace
process then calls exec() to execute the the program and arguments. This program is a child of the trace process,
but the fork() function copies its parents mask to the child. When the program argument executes it has its trace
mask set which prints the trace results.
In the first example trace runs grep tracing just the read system call. The 32 is 1<<SYS_read.
$ trace 32 grep hello README
3: syscall read -> 1023
3: syscall read -> 966
3: syscall read -> 70
3: syscall read -> 0
$
https://gusty.bike/labs/syscall.html 13/17
00:10 7/1/25 Lab: System calls
In the second example, trace runs grep while tracing all system calls; the 2147483647 has all 31 low bits
set.
$ trace 2147483647 grep hello README
4: syscall trace -> 0
4: syscall exec -> 3
4: syscall open -> 3
4: syscall read -> 1023
4: syscall read -> 966
4: syscall read -> 70
4: syscall read -> 0
4: syscall close -> 0
$
In the third example, the program isn't traced, so no trace output is printed.
$ grep hello README
$
In the fourth example the fork system calls of all the descendants of the forkforkfork test in usertests are
being traced. Your solution is correct if your program behaves as shown above (though the process IDs
may be different).
$ trace 2 usertests forkforkfork
usertests starting
test forkforkfork: 407: syscall fork -> 408
408: syscall fork -> 409
409: syscall fork -> 410
410: syscall fork -> 411
409: syscall fork -> 412
410: syscall fork -> 413
409: syscall fork -> 414
411: syscall fork -> 415
...
$
In this problem you will add a system call, sysinfo, that collects information about the
running system. The system call takes one argument: a pointer to a struct sysinfo (see
kernel/sysinfo.h). The kernel should fill out the fields of this struct: the freemem field
should be set to the number of bytes of free memory, and the nproc field should be set to
the number of processes whose state is not UNUSED. We provide a test program
sysinfotest that you can use to test your implementation
Some Hints
Add $U/_sysinfotest to UPROGS in Makefile. The file sysinfotest.c contains test cases for this
problem.
You could run make qemu; but user/sysinfotest.c will fail to compile because it calls the sysinfo system
call which is not available yet.
Add the system call sysinfo, following the same steps as in the previous problems of this lab. You can
refer to Reference: Xv6 System Calls for the steps. For one of the steps, you must declare the prototype for
sysinfo(struct sysinfo *) in user/user.h, but since sysinfo has a struct sysinfo * argument, you need
https://gusty.bike/labs/syscall.html 14/17
00:10 7/1/25 Lab: System calls
predeclare the existence of struct sysinfo in order for the prototype to compile correclty. Place the
following as the second line in the file user/user.h.
struct sysinfo;
Now you can place the following sysinfo function prototype in the list of system calls in the file
user/user.h.
Add a stub to user/usys.pl, and a syscall number to kernel/syscall.h. The Makefile invokes the perl
script user/usys.pl, which produces user/usys.S, the actual system call stubs, which use the RISC-V
ecall instruction to transition to the kernel.
Once you fix the compilation issues, you could run trace 32 grep hello README; but it will fail because
you haven't implemented the system call in the kernel yet.
Once you fix the compilation issues, make qemu will create a system with sysinfotest, but it will not run
because you haven't implemented the system call in the kernel yet.
Your sysinfo will need some helper functions in the kernel. These functions are not system calls because
only the kernel (namely sysinfo) calls them; however, you must put their function prototypes in defs.h.
kernel/defs.h - place this function prototypes for get_freemen() and get_nproc() in kernel/defs.h.
You will notice kernel/defs.h is organized by the .c files in the kernel.
Locate the kalloc.c section and add the following prototype. Follow the formatting of the file.
int get_freemem(void);
Locate the proc.c section and add the following prototype. Follow the formatting of the file.
int get_nproc(void);
get_freemem() - place this function in kernel/kalloc.c. Implementing this function requires you to
walk the free memory list counting the free bytes. It returns the number of bytes of free memory.
The free memory is pointed to by the variable kmem.freelist, which is type struct run *,
which is defined in kalloc.c as follows.
struct run {
struct run *next;
};
You must traverse the singly linked-list to compute the number of free bytes.
The free list is a list of free pages, where each page is PGSIZE bytes.
The macro PGSIZE is defined to be 4096 in the file riscv.h.
Notice that the free list has a lock kmem.lock that you must acquire before traversing the free
list and release after traversing the free list. We will study locks in detail shortly.
The following is the code for get_freemem(). It is a basic linked-list traversal algorithm that
accumulates the total number of free bytes. You can place this at the bottom of the file kalloc.c.
get_nproc() - place this function in kernel/proc.c. Implementing this function requires you to
iterate through the procs counting those that are not UNUSED. It returns the number of procs that are
not UNUSED.
Procs are stored in the array struct proc proc[NPROC];.
You must itterate through the array struct proc proc counting the procs that are not UNUSED.
The following is the code for get_nproc(). You can place this at the bottom of the file proc.c.
sysinfo must copy a struct sysinfo back to user space; see sys_fstat() (kernel/sysfile.c) and
filestat() (kernel/file.c) for examples of how to do that using copyout().
uint64
sys_sysinfo(void)
{
struct sysinfo info;
info.freemem = get_freemem();
info.nproc = get_nproc();
uint64 addr;
argaddr(0, &addr);
// kernel cannot directly access user space. use copyout()
return copyout(myproc()->pagetable, addr, (char*)&info, sizeof(info));
}
Testing
https://gusty.bike/labs/syscall.html 16/17
00:10 7/1/25 Lab: System calls
We have provided user/sysinfotest.c to test the sysinfo implementationa, and it is an execellent example of
how to test. The code in user/sysinfotest.c requires some thought to understand.
The function testcall() makes sure that sysinfo() succeeds with a valid address and fails with a bad
addres..
The function testmem() first counts all of the free pages of memory. It does this by calling countfree(),
which repeatedly consumes all of the free pages by calling the sbrk() function, counting the pages as they
are aquired. After countfree() has consumed all of the free memory, it calls sysinfo(), which should
return 0 bytes of free memory. Then countfree() returns all of the pages it has acquired and calls
sysinfo() again. This time sysinfo() should return the number of pages counted.
The function testproc() calls sysinfo() to determine how many process are not UNUSED. Then testproc()
calls fork() to create a child process. Then testproc() calls sysinfo() again, which should return one
more process than before. After the child process finishes, testproc() calls sysinfo() again, which should
return the original number of processes.
hart 2 starting
hart 1 starting
init: starting sh
$ sysinfotest
sysinfotest: start
sysinfotest: OK
$
https://gusty.bike/labs/syscall.html 17/17