Lab 3
Lab 3
● Note: Submission guidelines to be strictly followed; otherwise, your submission will not count.
● Login with username your CSE LDAP ID or labuser on software lab machines for this lab.
● This file is part of the lab2.tar.gz archive which contains multiple directories with programs
associated with the exercise questions listed below.
● Most questions of this lab are tutorial style and are aimed to provide an introduction to tools and
files related to system and process information and control.
In this lab we will learn how to use the xv6 operating system, learn how system calls are
implemented, explore examples of OS metadata and actions and dabble with the memory
management subsystem.
Write a program named pingpong which reads a text file as an input argument and outputs
pong to the standard output every time it finds the word ping in the input text file.
I. Additions to the Makefile will be needed to add new programs for compilation and also
to be included as part of the xv6 viewable disk image (to read/write files e.g., abc.txt,
hello.txt) via fs.img
Look for the following keywords in the Makefile.
UPROGS=\
Lists names of all user programs which are available after xv6 boot up.
EXTRA=\
List of all files (source programs and other scripts and data files) available after xv6
bootup.
fs.img
List of files to be added to the xv6 startup disk (imagefile).
II. xv6 OS itself does not have a text editor or compiler support, all source code of
programs has to be written and compiled on the host machine, all its references added
to the makefile and then via fs.img and xv6.img be used via QEMU emulator.
III. You will need to include input text files for e.g., pingpong.txt or any other files in the
xv6 OS image that you will use for running the program.
Refer to the README included in xv6 image.
IV. Sample input file pingpong.txt is provided as part of this lab archive file.
Consider using wc.c, source code of the wc program as a starting point for this task.
Sample usage
Write a program cmd.c that creates a child process — the child process executes a program,
and the parent process waits till completion of the child process before terminating. This
program should use the fork and exec system calls of xv6. The program to be executed by the
child process can be any of the sample xv6 programs and should be specified at the command
line.
Refer to Sheet 66,85 of xv6 source code booklet for fork(), exec() system calls in xv6.
Sample usage
Note that xv6 itself does not have a text editor or compiler support, so all xv6 source code
changes are on the host machine and then the xv6.img and fs.img files are used as inputs to
QEMU.
Implement a system call, with the following declaration worldpeace(), which prints the message
“Systems are vital to world peace !!” in the kernel mode.
The function cprintf is used for printing in the kernel mode (refer to sheet 30 line 3026 of xv6
source code booklet for usage).
A simple test program worldpeace.c is also provided to test your implementation.
ChatGPT’s take on systems + world peace is here.
Sample usage
Implement a system call, with the following declaration numberofprocesses() which returns the
total number of processes in READY(xv6 naming is RUNNABLE) state to the user program.
Refer to the PCB structure defined on line 2336 sheet 23 of xv6 source code booklet and lines
10-14 in proc.c in the given source code to refer to struct ptable.
Refer to sheet 24 Line 2480 to understand how to iterate through the process table and sheet
23 Line 2334 to for the process state enum of xv6 source code booklet .
Sample usage
(c) spawn — one call, many processes!
Implement a system call, with the following declaration int spawn(int n, int* pids)
which creates n child processes when invoked.
● The system call must return 0 to each of the child processes and number of children
created to the parent process.
● Additionally, the pids array should contain the pids of the spawned children after the
spawn system call. The parent should gracefully reap all the child processes which are
present in the pids array.
Note: argint, argstr, argptr are helper functions for handling system calls arguments.
(Refer to sheet 65 line 6557 of xv6 source code booklet for argptr usage)
Refer to fork() system call implementation in proc.c to understand how a child process is
created and how the call handles return values for parent and child processes.
Sample usage
Write a system call getvasize that returns the size of the address space used by a process.
Specifically, the system call should have the following interface:
Figure1: Address space layout of process in xv6. User stack is of one page, followed by the
heap up to KERNBASE. What is the value of the constant KERNBASE?
Write a system call va2pa that returns the virtual address to physical address mapping from the
page table of the current process. Specifically, the system call should have the following
interface:
uint va2pa(uint virtual_addr);
// virtual address is the argument
// corresponding physical address is the return value
Hints:
(i) Lookup and understand the walkpgdir() function and understand usage of this function in
the system calls implemented in vm.c
(ii) Refer to Sheet 17 of xv6 source code booklet for sbrk() system calls in xv6.
(iii) Refer to discussion on Page 29-32 of the xv6 book.
Figure 2. A page table is stored in physical memory as a two-level tree. The root of the tree is
a 4096-byte page directory that contains 1024 PTE references to page table pages. Each page
table page is an array of 1024 32-bit PTEs. The paging hardware uses the MSB 10 bits of a
virtual address to select a page directory entry. If the page directory entry is present, the paging
hardware uses the next 10 bits of the virtual address to select a PTE from the page table page
that the page directory entry refers to. If either the page directory entry or the PTE is not
present, the paging hardware raises a fault.
Figure 3. A process’s address space starts at virtual address zero and can grow up to
KERNBASE, allowing a process to address up to 2 GB of memory. The file memlayout.h
declares the constants for xv6’s memory layout, and macros to convert virtual to physical
addresses. When a process asks xv6 for more memory, xv6 first finds free physical pages
from the free page list and then adds PTEs to the process’s page table that point to the new
physical pages. xv6 sets the PTE_U, PTE_W, and PTE_P flags in these PTEs. xv6 includes all
mappings needed for the kernel to run in every process’s page table; these mappings all
appear above KERNBASE.
Use the above information to traverse the page table of a process and convert virtual address
to physical address.
Sample usage:
Implement the following system calls to get details of the page table of a process.
int get_pgtb_size();
The system call has no arguments and returns the number of page table pages allocated to the
current process.
int get_usr_pgtb_size();
The system call has no arguments and returns the number of page table pages allocated for
user space memory for the current process.
int get_kernel_pgtb_size();
The system call has no arguments and returns the number of page table pages allocated for
kernel space memory for the current process. Recall kernel pages are mapped for virtual
addresses above KERNBASE.
A user-level program t_getpgtablesz.c is provided for testing. This program will call all the
above system calls before and after multiple sbrk() system calls.
Hint:
Walk the page table of a process based on/using the walkpgdir function and consider only
those entries which indicate mappings that are present (the present bit is set).
Sample usage
The call takes pid as an argument and prints the number of physical pages mapped to the
virtual addresses of a process (process virtual addresses).
NOTE: Count the number of mapped pages by walking the process page table and counting the
number of page table entries that have a valid physical address assigned.
Sample usage
We will use these system calls to test your implementation in the next task.
Hints:
(i) You can walk the page table of the process by using the walkpgdir function which is present
in vm.c. You can look up loaduvm and deallocuvm in vm.c to see how to invoke the
walkpgdir function. To compute the number of physical pages in a process, you can write a
function that walks the page table of a process in vm.c and invoke this function from the system
call handling code.
(ii) xv6 has a 2-level page table organization. You need to calculate the size of the page table
(total level 0 and level 1 pages). You need to iterate over the Page Directory Entries (PDEs) to
check if a page is assigned for storing Page Table Entries (PTEs) for that PDE.
Refer to Sheet 34 of xv6 source code booklet for trap() handler in xv6.
Sample usage
Hints:
● Look at the arguments to the cprintf statements in trap.c to figure out how one can find
the virtual address that caused the page fault.
● Once you correctly handle the page fault, do break or return in order to avoid the cprintf and
the add proc->killed = 1 statement.
(b) mmap()
Implement a version of the mmap system call in xv6. The mmap system call should take one
argument: the number of bytes to add to the size of the process. The process size in this
context refers to the heap size. The mmap call grows the size of the process (virtual) address
space and expects a mapped physical address.
However, mapping from a virtual to physical address is required only when the virtual
address is accessed!.
Figure 4 shows the working of the mmap system call.The mmap(1024) call increases the
virtual address space of the process, however the physical address space remains the same.
Assume that the number of bytes is a positive number and is a multiple of the page size. The
system call should return a value of 0 if any invalid inputs are provided.
In the valid case, the system call should expand the process's size by the specified number of
bytes, and return the starting virtual address of the newly added memory region.
However, the system call should NOT allocate any physical memory corresponding to the
new virtual pages. When the user accesses a memory-mapped page, a page fault will occur,
and physical memory should only be allocated as part of the page fault handling (lazy page
allocation and mapping).
Hints:
(i) mmap() system call is similar to sbrk() (with code related to memory allocation and mapping
pages … kalloc, growproc, allocuvm, mappages etc.)
(ii) Understand the implementation of the sbrk system call and mmap() system call will follow a
similar logic.
(iii) Refer to Sheets 19, 25, 38 of xv6 source code booklet for the related system calls.
(iv) Refer to Page 34 of the xv6 book.
Sample usage
Hints:
● Look at the arguments to the cprintf statements in trap.c to figure out how one can find the
virtual address that caused the page fault.
● Use PGROUNDDOWN(va) to round the faulting virtual address down to the start of a page
boundary.
● You may invoke allocuvm (or write another similar function) in vm.c in order to allocate
physical memory upon a page fault.
● You can add your page fault handler in vm.c and call it from trap.c.
● Check whether the page fault was actually due to a lazy allocated page or an actual page
fault (For example - illegal memory access).
Note: it is important to call switchuvm to update the CR3 register and TLB every time you
change the page table of the process. This update to the page table will enable the process
to resume execution when you handle the page fault correctly.
Submission Instructions
● All submissions are via moodle. Name your submission as <rollnumber_lab3>.tar.gz
(e.g 190050096_lab3.tar.gz)
● The tar should contain the xv6 source code files in the following directory structure:
<rollnumber_lab3>/xv6
● The directory must contain the new programs pingpong.c and cmd.c which you will
implement as a part of 1a and 1b respectively and the entire xv6 source code with
necessary changes to it.
● Your code should be well commented and readable.
● tar -czvf <rollnumber_lab3>.tar.gz <rollnumber_lab3>
Sample usage
$ my_siblings 6 1 2 1 0 2 0
4 RUNNABLE
5 ZOMBIE
6 RUNNABLE
7 SLEEPING
8 ZOMBIE
9 SLEEPING
Sample user program my_siblings.c is provided. The program takes an integer n, followed by a
combination of 0, 1 and 2 of length n, as command line arguments— 0/1/2 specify the process
state of the n child processes. The (n+1) th child process executes the get_sibling() system call
and displays the output.
Hint: You need to find the process ID of the calling process, and process ID of its parent and
traverse all the PCBs and compare their parent PID with the parent of the calling process.
Sample usage
2. entrypoint 2.0
In order to achieve this you should first need to look at how system calls are handled in xv6.
List of files you will need to refer to:
sysc.all.c syscall.h defs.h user.h proc.c sysproc.c proc.h trap.c trap.h usys.S
Note: First you need to write a trap handler and after that you can implement a new type of
system call. (TRAP NUMBER for SYSCALL is 64 but the actual system call number of
system call like fork in xv6 is 1).
Hint: usys.s has the entry point from where int n is called. :)
One way to implement this is to change only the entry point to the trap handler and use the
same underlying system call implementation for all system calls.
Implement a system call, with the following declaration whatsthestatus(int pid), which returns
the parent pid, prints the name of the parent process and the current state of the process
given the pid of the process.
The function cprintf is used for printing in the kernel mode (refer to sheet 30 line 3026 of xv6
source code booklet for usage).
Please refer to the PCB structure defined on line 2336 sheet 23 of xv6 source code booklet
and lines 10-14 in proc.c in the given source code to refer to ptable struct.
Refer to sheet 24 Line 2480 to understand how to iterate through the process table and sheet
23 Line 2334 to understand the process enum structure of xv6 source code booklet .
Refer to sheet 36 line 3631-3632 to refer argint usage of xv6 source code booklet
Note: argint, argstr, argptr are helper functions for handling system calls arguments.
Output should be in the format <pid> <status> <ppid> <parent_name> where pid represents
the pid of the child process for which status needs to be checked, ppid represents the parent
pid and parent_name represents the name of the parent.
Sample usage