The aim of this milestone is to design the RPC
protocol for the system call interface. You should implement both the
client and system side of this interface. This client-side
system-call interface must conform to the interface provided in
projects/aos/libsosapi/include
. You will be adding to
the code in projects/aos/libsosapi/src/sos.c
that will
eventually contain the implementation of the client-side
sos.h
interface within the library.
musl libc and sos
libsosapi
defines two kinds of syscalls. Some are
prefixed with sos_sys_
and others are prefixed with just
sos_
. Syscalls starting with sos_sys_
are
called as a result of invoking musl C library functions. Syscalls
starting with just sos_
have no C library wrapper and
your application code will either invoke them directly, or you can
write your own wrappers.
For example, the shell does not call sos_sys_open
directly. Instead it uses the standard C library open
function (it could also have used fopen
), musl libc will
translate this into an internal call to a POSIX style
open
syscall, which is implemented in the
sys_open
function in
projects/aos/libsosapi/src/sys_stdio.c
. This function is
a small wrapper to extract arguments out of a va_list
and
call your sos_sys_open
function.
Not all syscalls have been implemented through musl libc as in some
cases the POSIX semantics are unnecessarily complex. You are free to
implement more of the functionality in any of the syscalls defined in
projects/aos/libsosapi/src/sys_*.c
or implement
additional syscalls by initialising them in
sosapi_init_syscall_table
.
At this stage you will not actually be able to implement most of the
SOS (server-side) system calls, however you should be able to
partially implement sos_sys_open/sos_sys_close,
sos_sys_read/sos_sys_write
for the console
device.
You should also implement sos_sys_usleep
and
sos_sys_time_stamp
. In other words, you need to modify
your timer driver to be able to sleep and wake up sosh. This will
allow you to run a simple shell on your system, which will allow you
to perform interactive testing.
Other system calls defined in sos.h
should output
system call not implemented
.
Console device
When a program opens the file console
it should access
the console on the serial device. The console is a multiple writer,
single reader device, i.e., more than one process can concurrently
open the device for writing, but only one process can open the device
for reading.
Reading the console is a blocking operation. If a process reads from the console and there is no data available it should block until data becomes available. If a new line character is encountered, the data returned should include all characters up to and including the new line character. Any remaining characters should be returned on the next read.
Be careful not to implement the console device as a 'hack'. You should think about being able to support multiple serial ports and other stream devices in your design (although not necessarily implement them). This means designing a consistent interface for interacting with all devices. You may want to read up on how Linux treats devices.
You may once again find the documentation on libserial
handy.
Getting started
You will need to modify the libsosapi library to add implementations
of the interface defined in
projects/aos/libsosapi/include/sos.h
. You should copy
the contents of tty_test/ttyout.c to
projects/aos/libsosapi/src/sos.c to import your sos_write
implementation from M0.
In order to load the sosh
app instead of
tty_test
you should pass "sosh"
to
start_first_process
instead of "tty_test"
.
Design alternatives
At this stage of the project you will need to decide whether you want to have a simple single-threaded server, or to multi-thread it. A multi-threaded design could be advantageous to deal with the inherent concurrency your system will have (e.g. between paging, system calls, asynchronous I/O and clock interrupts), but it will require careful design of synchronisation in order to avoid race conditions and deadlocks. A single threaded model will require extra attention to ensure liveness.
Another design decision is how to transfer data between the kernel and user processes. Some options you have are:
- Transfer data in message registers.
- sos maps a user page in to itself.
-
Set up a region of memory which is shared between the kernel and
each user process and
memcpy
data into it when you need to transfer data. - Use copyin-copyout method, where you lookup the page table and your system copies data in to/out of physical memory (i.e. all frames are also mapped into SOS).
Whatever you do, remember the basic engineering rule: keep it simple, stupid! (KISS).
Advice
This milestone is larger than it seems. The system call interface of an OS determines how user applications receive data they request. You will need to consider how you can move data in various quantities between your root server and clients. Scenarios to consider include:
- Getting system call arguments from client to server. These are generally a few words long.
- Getting filesystem call arguments from client to server. Mentioned specially because filenames can be long.
Recall also that the OS can't rely on applications for correctness. Any system call you add should behave gracefully in the presence of a malicious application - i.e. use error checking on system call arguments, and returns errors when encountering problems.
Later, when you OS is managing multiple concurrent processes, you will need to be able to service operations for processes in various different states. This will involve having one or more processes blocked waiting for responses to system calls (such as timer and file operations) whilst other processes must continue to run. You will also need to be able to stop and remove processes that may either be blocked or running.
It is important to think about how you will represent processes that are blocked and waiting on system calls so that these situations are handled correctly.
Assessment
Demonstration
For this assessment you should be able to demonstrate
sosh
running, and SOS outputting system call not
implemented
for the relevant SOS system calls.
sosh
makes use of the SOS system calls in its
ls
and ps
commands. This should be
sufficient to demonstrate that your SOS system calls work. Of course,
you can also write your own test code, but ensure that your
solution also works with an unmodified sosh
.
You should also show some sos application-level test code that
uses the time_stamp
and sleep
system
calls. sosh
also has some sample commands for sleeping
and getting the current time via different libc functions.
The sleep
implementation must use your
clock driver.
The code below is the minimum demo to pass the milestone. Note that this code is not comprehensive, for best chances in the final marking you should test your sos_ interface extensively (edge and error cases).
#define SMALL_BUF_SZ 2 #define MEDIUM_BUF_SZ 256 char test_str[] = "Basic test string for read/write"; char small_buf[SMALL_BUF_SZ]; int test_buffers(int console_fd) { /* test a small string from the code segment */ int result = sos_sys_write(console_fd, test_str, strlen(test_str)); assert(result == strlen(test_str)); /* test reading to a small buffer */ result = sos_sys_read(console_fd, small_buf, SMALL_BUF_SZ); /* make sure you type in at least SMALL_BUF_SZ */ assert(result == SMALL_BUF_SZ); /* test reading into a large on-stack buffer */ char stack_buf[MEDIUM_BUF_SZ]; /* for this test you'll need to paste a lot of data into the console, without newlines */ result = sos_sys_read(console_fd, &stack_buf, MEDIUM_BUF_SZ); assert(result == MEDIUM_BUF_SZ); result = sos_sys_write(console_fd, &stack_buf, MEDIUM_BUF_SZ); assert(result == MEDIUM_BUF_SZ); /* try sleeping */ for (int i = 0; i < 5; i++) { time_t prev_seconds = time(NULL); second_sleep(1); time_t next_seconds = time(NULL); assert(next_seconds > prev_seconds); printf("Tick\n"); } }
As always, you should be able to explain both the design and your implementation.
Show Stoppers
-
Implementations of
read
andwrite
that need to check whether the file being accessed is the console (e.g. viastrcmp
). (open
will at some point need to use some different implementation to open a file for the console device rather than an NFS file). - Masses of code in a giant
switch
statement. - Assuming only one application can perform I/O at a time (i.e. waiting for I/O completion before potentially accepting another application call).
Better Solutions
- Have a clear framework for handling all blocking within SOS in a consistent way.