Lab01_OS
Lab01_OS
GROUP PROJECT
Xv6 and Unix utilities
OPERATING SYSTEM
Team member
22125096 − Đoàn Công Thành
Contents
Page
1 Introduction 2
2 Ping-Pong 2
2.1 Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.3 Challenging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3 Primes 3
3.1 Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3.3 Challenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
4 Find 4
4.1 Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4.3 Challenging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5 Xargs 4
5.1 Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
5.3 Challenging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1
Lab 01 - Operating System
1 Introduction
This report illustrates the problems we worked on, the solutions we created, and the results we achieved.
It describes the goals of each task, how we solved the problems, and any difficulties we faced.
2 Ping-Pong
2.1 Objective
The goal was to implement a user-level program that uses xv6 system sends a byte between two processes
using pipes and prints relevant messages as specified.
2.2 Solution
First, we use the pipe(int p[]) function to create two pipes: one for communication from the parent to
the child and another for communication from the child to the parent. Each pipe is an array of two file
descriptors: one for reading (p[0]) and one for writing (p[1]).
int p [2];
int c [2];
pipe ( c ) ; // pipe for child - parent communication
pipe ( p ) ; // pipe for parent - child communication
After that, we use the fork() system call to create a new child process which copies the instructions,
data, and stack of the calling process into the new process’s memory.. Both processes then run in parallel,
with the program’s behavior varying depending on whether it executes in the parent or child process:
In child process:
• Read a byte from the parent using the parent-to-child pipe (p[0] for reading). If the read is
successful, use the getpid() function to get the process ID and print "received ping" along with
the process ID.
• Write the same byte back to the parent using the child-to-parent pipe (c[1] for writing).
In parent process:
• Write a byte to the child using the parent-to-child pipe (p[1] for writing).
• Wait for the child to complete using the wait() system call to ensure synchronization.
• Read the byte returned by the child using the child-to-parent pipe (c[0] for reading).If the read is
successful, use the getpid() function to get the process ID and print "received pong" along with
the process ID.
2
Lab 01 - Operating System
2.3 Challenging
This problem requires a deep understanding of several new concepts, such as pipe, fork, and wait,
which can be challenging initially.
3 Primes
3.1 Objective
The objective was to implement a concurrent prime sieve program for the xv6 operating system using
pipes and processes, inspired by Doug McIlroy, the inventor of Unix pipes. The program generates all
prime numbers from 2 to 280 by creating a pipeline. Each process in the pipeline filters out multiples of
its assigned prime number.
3.2 Solution
This problem extends the use of two key concepts—pipe(int p[]) and fork() —which were used in
earlier problems. We create a series of child and paretnt processes. The parent and child processes
run concurrently. As the parent writes numbers to the pipe, the child immediately starts reading and
processing them. The child processes are created recursively as each prime is identified, forming a chain
of processes that work in parallel to filter the numbers.
Parent Process:
• The parent process creates a pipe and forks a child process to begin the prime sieve.
• It writes numbers from 2 to 280 into the pipe, one by one. These numbers are then passed through
the pipeline of child processes for filtering.
• In this process, we must have wait(), to ensure that the parent process exit after child process
has completed.
Children Process
• The first element is considered prime number, so print it. Then, creating a new pipe for the next
stage of filtering and forks another process.
• Each subsequent child process continues the filtering process, eliminating numbers divisible by
the prime assigned to it, and passes the remaining numbers down the pipeline. The filtering stops
when no more numbers are left to process.
3.3 Challenge
In this problem, the most challenging aspect is understanding the flow of the program and how the
processes interact with each other. Additionally, since xv6 has a limited number of file descriptors,
proper management is critical. Specifically, we need to ensure that file descriptors are closed after they
are no longer needed to prevent running out of file descriptors, which could lead to crashes.
3
Lab 01 - Operating System
4 Find
4.1 Objective
This project was done for implementing a user-level program in xv6 that mimics the UNIX find utility.
This finds and prints the paths of all files in a given directory tree with a specified name.
4.2 Solution
For this, we first extended the utility of ls in the xv6 user library and developed a recursive way of
traversing the file structure. Briefly, it does the following:
1. Path Processing: The program receives a path as input and attempts to open it. If the file or
directory is successfully opened, it retrieves metadata using the fstat() function to determine
the file type. There are three possible types:
2. Directory Traversal:
• For directories, the program creates a buffer to store the current path and appends the name
of each directory entry (de.name) to traverse the directory tree.
• It explicitly skips the entries . and .. to avoid infinite recursion.
• The function find() is recursively called for every directory entry, with the path updated for
each call. This process continues until the entire directory tree is traversed.
3. Error Handling:
• The program handles errors such as failure to open a file or directory, inability to retrieve
metadata, or exceeding the path length buffer limit.
• In such cases, appropriate error messages are displayed to the user.
4.3 Challenging
This problem is relatively straightforward. The main effort lies in understanding the implementation of
ls.c and applying its logic to solve the task.
5 Xargs
5.1 Objective
The objective was to implement a simplified version of the UNIX xargs utility for xv6. It takes a
command and its arguments as input, reads lines from standard input, and appends each line to the
command’s arguments before executing it.
4
Lab 01 - Operating System
5.2 Solution
To achieve the objective, the program follows these steps:
1. Argument Handling: The command and its arguments are extracted from the argv array. If the
size of argv is less than 2, the program immediately returns, as this is an invalid command line
for xargs (no command is provided to execute).If the size of argv is valid, the program copies the
elements of the argv array starting from index 1 (skipping index 0, which contains xargs itself) into
the cmd array. This ensures that the cmd array contains the command and its initial arguments,
ready to append input lines read from standard input.
• Characters are read one by one until a newline (\n) or the end-of-input is encountered,
signaling the end of a line.
• Once a line is read, it is appended to the end of the cmd array.
• A fork() is created, and the child process uses exec() to execute the updated command.
• The parent process waits for the child process to complete. Afterward, the buffer size for the
current input is reset, preparing to read the next line.
• This process continues until all input lines have been read and processed.
5.3 Challenging
The most challenging part of this problem is managing input reading while ensuring that the
arguments are correctly appended to the command. Additionally, when executing the command
with exec(), it is essential to include wait(0) in the parent process to ensure it waits for the child
process to complete before proceeding.