[CSE]  Advanced Operating Systems 
COMP9242 2016/S2 
UNSW
CRICOS Provider
Number: 00098G

PRINTER Printer-Friendly Version

M4: System call interface

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 libs/libsos/include. You will be adding to the code in libs/libsos/src/sos.c that will eventually contain the implementation of the client-side sos.h interface within the library.

musl libc and sos

libsos 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 libs/libsos/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 libs/libsos/src/sys_*.c or implement additional syscalls from libs/libsos/src/sys_stubs.c

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 be able to 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.

You should also change the implementation of sys_brk in libs/libsos/src/sys_morecore.c to dynamically manage the heap memory region by implementing your own system call to the in SOS to set the brk point for the virtual address space.

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.

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 libsos library to add implementations of the interface defined in libs/libsos/include/sos.h. You should copy the contents of tty_test/ttyout.c to libs/libsos/src/sos.c to import your sos_write implementation from M0.

You will also need to add sosh to the cpio archive that is linked to SOS and instruct SOS to run this application. Both of these tasks are achieved through the configuration menu. Run $ make menuconfig and navigate to the Applications submenu. Move the curser to the the SOSH application and hit space to select it. Next, expand the sub menu for the SOS application by navigating to the SOS application and hitting enter. Finally, navigate to the Startup application option and hit enter to modify the option. Don't forget to save the configuration when you exit!

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.
  • Getting filesystem data from client to server (for read()) or server back to client (for write()). This can be really big. Streaming it a few words at a time over IPC will be sloooooow. Do not do it.

Recall how OS/161 used an internal framework for safely transporting data between the OS and the application. The OS avoided simply reaching into application address spaces based on app-supplied pointers. You'll need to think about a framework (or at least a convention within your code) that can provide SOS with safe access to application memory. In later milestones, your approach will have to evolve to function in the presence in paging to disk, including paging application buffers.

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 encoutering problems.


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 implemention must use your clock driver.

Demonstrate your sbrk system call implementation increasing the application heap size during repeated application malloc calls.

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

   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 a reading into a large on-stack buffer */
       char stack_buf[BUF_SIZ];
       /* 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, BUF_SIZ);
       assert(result == BUF_SIZ);

       result = sos_sys_write(console_fd, &stack_buf, BUF_SIZ);
       assert(result == BUF_SIZ);

       /* this call to malloc should trigger an sbrk */
       char *heap_buf = malloc(BUF_SIZ);
       assert(heap_buf != NULL);

       /* for this test you'll need to paste a lot of data into 
          the console, without newlines */
       result = sos_sys_read(console_fd, &heap_buf, BUF_SIZ);
       assert(result == BUF_SIZ);

       result = sos_sys_write(console_fd, &heap_buf, BUF_SIZ);
       assert(result == BUF_SIZ);

       /* 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

  • Implementing hacky access to devices (e.g. if (!strcmp(name,"console")) {/* device code path that is not 'open'*/}).
  • Implementing bulk transfer between applications and SOS via repeated IPC calls each transferring an IPC buffer's worth of data.
  • Masses of code in a giant switch statement.
  • Assuming buffers are always smaller than a page.
  • Allow applications to specify buffers outside valid memory ranges
  • Assuming only one application can perform I/O at a time (i.e. waiting for I/O completion before potentially accepting another application call).
  • Assuming the user buffer provided to sos_read or sos_write is resident (if the buffer is allocated with malloc, or high on the stack, and the user application has not touched it yet, it will need to be mapped in by your page fault handler).

Better Solutions

  • Have a clear framework for handling all blocking within SOS in a consistent way.
  • Only make one system call (between sos and user-level) for large buffers.
  • Be able to handle user buffers that are larger than the physical memory size.

Last modified: 13 Sep 2016.