Implementing Unix Utilities in Xv6: Sleep, Ping-Pong, Primes, Find, and Xargs
Sleep
A simple utility to pause execution for a specified number of seconds.
#include "kernel/types.h"
#include "user/user.h"
int main(int argument_count, char *argument_values[])
{
if (argument_count < 2)
{
fprintf(2, "Usage: sleep <seconds>\n");
exit(1);
}
int result = sleep(atoi(argument_values[1]));
if (result == -1)
{
exit(1);
}
exit(0);
}

Ping-Pong
A program demonstrating inter-process communication using pipes where two processes exchange a single byte.
#include "kernel/types.h"
#include "user/user.h"
int main(void)
{
int pipe_to_child[2];
int pipe_to_parent[2];
int process_id;
char data_buffer[1] = {'X'};
pipe(pipe_to_child);
pipe(pipe_to_parent);
process_id = fork();
if (process_id == 0) // Child process
{
close(pipe_to_child[0]);
close(pipe_to_parent[1]);
write(pipe_to_child[1], data_buffer, sizeof(data_buffer));
printf("%d: received ping\n", getpid());
read(pipe_to_parent[0], data_buffer, sizeof(data_buffer));
}
else // Parent process
{
close(pipe_to_child[1]);
close(pipe_to_parent[0]);
read(pipe_to_child[0], data_buffer, sizeof(data_buffer));
printf("%d: received pong\n", getpid());
write(pipe_to_parent[1], data_buffer, sizeof(data_buffer));
}
exit(0);
}

Primes
A concurrent prime number sieve using multiple processes connceted by pipes.
#include "kernel/types.h"
#include "user/user.h"
void sieve_prime_numbers(int input_pipe)
{
int current_number;
if (read(input_pipe, ¤t_number, 4) == 0)
{
close(input_pipe);
return;
}
printf("prime %d\n", current_number);
int output_pipe[2];
pipe(output_pipe);
int child_pid = fork();
if (child_pid > 0) // Parent process
{
close(output_pipe[0]);
int candidate_number;
while ((read(input_pipe, &candidate_number, 4)) != 0)
{
if (candidate_number % current_number != 0)
{
write(output_pipe[1], &candidate_number, 4);
}
}
close(output_pipe[1]);
wait(0);
}
else // Child process
{
close(output_pipe[1]);
sieve_prime_numbers(output_pipe[0]);
}
}
int main(void)
{
int initial_pipe[2];
pipe(initial_pipe);
int first_child = fork();
if (first_child > 0) // Parent process
{
close(initial_pipe[0]);
for (int number = 2; number <= 35; number++)
{
write(initial_pipe[1], &number, 4);
}
close(initial_pipe[1]);
}
else // Child process
{
close(initial_pipe[1]);
sieve_prime_numbers(initial_pipe[0]);
}
exit(0);
}

Find
A file search utility that recursively traverses directories to locate files matching a given name.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
#include "kernel/fs.h"
void search_files(char *search_path, char *target_filename)
{
char path_buffer[512], *path_pointer;
int file_descriptor;
struct dirent directory_entry;
struct stat file_status;
if ((file_descriptor = open(search_path, O_RDONLY)) < 0)
{
fprintf(2, "find: cannot open %s\n", search_path);
return;
}
if (fstat(file_descriptor, &file_status) < 0)
{
fprintf(2, "find: cannot stat %s\n", search_path);
close(file_descriptor);
return;
}
if (file_status.type == T_FILE || file_status.type == T_DEVICE)
{
for (path_pointer = search_path + strlen(search_path);
path_pointer >= search_path && *path_pointer != '/';
path_pointer--)
;
path_pointer++;
if (strcmp(path_pointer, target_filename) == 0)
{
printf("%s\n", search_path);
}
close(file_descriptor);
return;
}
strcpy(path_buffer, search_path);
path_pointer = path_buffer + strlen(path_buffer);
*path_pointer++ = '/';
while (read(file_descriptor, &directory_entry, sizeof(directory_entry)) == sizeof(directory_entry))
{
if (directory_entry.inum == 0 ||
strcmp(directory_entry.name, ".") == 0 ||
strcmp(directory_entry.name, "..") == 0)
{
continue;
}
memmove(path_pointer, directory_entry.name, DIRSIZ);
path_pointer[DIRSIZ] = 0;
search_files(path_buffer, target_filename);
}
close(file_descriptor);
}
int main(int argument_count, char *argument_values[])
{
if (argument_count < 2)
{
fprintf(2, "Usage: find <path> <filename>\n");
exit(1);
}
else if (argument_count == 2)
{
search_files(".", argument_values[1]);
exit(0);
}
search_files(argument_values[1], argument_values[2]);
exit(0);
}

Xargs
A utility that reads lines from stadnard input and executes a command with those lines as arguments.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
#include "kernel/param.h"
#define BUFFER_SIZE 512
int main(int argument_count, char *argument_values[])
{
int bytes_read;
char input_buffer[BUFFER_SIZE];
char *execution_arguments[MAXARG];
if (argument_count < 2)
{
execution_arguments[0] = "echo";
}
else
{
execution_arguments[0] = argument_values[1];
}
for (int i = 2; i < argument_count; i++)
{
execution_arguments[i - 1] = argument_values[i];
}
while ((bytes_read = read(0, input_buffer, sizeof(input_buffer))) > 0)
{
char argument_string[512] = {'\0'};
char *line_end;
int line_length;
while (bytes_read > 0)
{
line_end = strchr(input_buffer, '\n');
line_length = line_end - input_buffer;
memmove(argument_string, input_buffer, line_end - input_buffer);
int child_pid = fork();
if (child_pid < 0)
{
fprintf(2, "Error: fork failed\n");
exit(1);
}
else if (child_pid == 0)
{
execution_arguments[argument_count - 1] = argument_string;
exec(execution_arguments[0], execution_arguments);
}
wait(0);
memmove(input_buffer, input_buffer + line_length + 1, line_length + 1);
bytes_read -= (line_length + 1);
}
}
if (bytes_read < 0)
{
fprintf(2, "xargs: read error\n");
exit(1);
}
exit(0);
}

Makefile Configuration
To integrate these utilities into the Xv6 system, add them to the UPROGS list in the Makefile:
UPROGS=\
$U/_cat\
$U/_echo\
$U/_forktest\
$U/_grep\
$U/_init\
$U/_kill\
$U/_ln\
$U/_ls\
$U/_mkdir\
$U/_rm\
$U/_sh\
$U/_stressfs\
$U/_usertests\
$U/_grind\
$U/_wc\
$U/_zombie\
$U/_sleep\
$U/_pingpong\
$U/_primes\
$U/_find\
$U/_xargs