You are on page 1of 12

B ROWSIX: Bridging the Gap Between Unix and the Browser

Bobby Powers, John Vilk, Emery D. Berger


University of Massachusetts Amherst

Abstract As a representative example, websites like ShareLaTeX1


and Overleaf2 let users write and edit LATEX documents in the
Applications written to run on conventional operating sys-
browser without the need to install a TEX distribution locally.
tems typically depend on OS abstractions like processes, pipes,
This workflow lowers the barrier for students and first-time
signals, sockets, and a shared file system. Porting these ap-
LATEX authors and enables real-time collaboration, eliminating
plications to the web currently requires extensive rewriting
arXiv:1611.07862v1 [cs.OS] 23 Nov 2016

some of the complexity of creating multi-author documents.


or hosting significant portions of code server-side because
These applications achieve this functionality by providing a
browsers present a nontraditional runtime environment that
browser-based frontend for editing; user input is sent to the
lacks OS functionality.
server for persistence and collaboration purposes. When the
This paper presents B ROWSIX, a framework that bridges
user requests a generated PDF, the website runs pdflatex
the considerable gap between conventional operating systems
and bibtex server-side on the users behalf, with the resulting
and the browser, enabling unmodified programs expecting a
PDF sent to the browser when complete.
Unix-like environment to run directly in the browser. B ROWSIX
comprises two core parts: (1) a JavaScript-only system that These web applications generate PDFs server-side out of
makes core Unix features (including pipes, concurrent pro- necessity because browsers lack the operating system services
cesses, signals, sockets, and a shared file system) available to and execution environment that Unix programs expect. Creat-
web applications; and (2) extended JavaScript runtimes for C, ing PDFs from LATEX requires spawning multiple processes to
C++, Go, and Node.js that support running programs written run pdflatex and bibtex, which need to read from and write
in these languages as processes in the browser. B ROWSIX to a shared file system. If PDF generation takes too long and
supports running a POSIX shell, making it straightforward to the user cancels the request, the application needs to send a
SIGTERM or SIGKILL signal to clean up any running processes.
connect applications together via pipes.
We illustrate B ROWSIXs capabilities via case studies that If PDF generation encounters an error, the application needs to
demonstrate how it eases porting legacy applications to the pipe the output of the relevant process back to the client over
browser and enables new functionality. We demonstrate a the network. Since browsers do not support processes, signals,
B ROWSIX-enabled LATEX editor that operates by executing un- pipes, sockets, or a shared filesystem, they cannot perform any
modified versions of pdfLaTeX and BibTeX. This browser-only of these steps without program modification.
LATEX editor can render documents in seconds, making it fast Previous attempts to cope with this impedance mismatch
enough to be practical. We further demonstrate how B ROWSIX between conventional applications and the browser fall short
lets us port a client-server application to run entirely in the of providing the environment needed by many programs (see
browser for disconnected operation. Creating these applica- Section 7). Emscripten and Doppio provide a POSIX-like run-
tions required less than 50 lines of glue code and no code time system for single processes, including a single-process
modifications, demonstrating how easily B ROWSIX can be file system, limited support for threads, synchronous I/O, and
used to build sophisticated web applications from existing proxying support for TCP/IP sockets [9, 12]. While these
parts without modification. single-process runtimes are useful for some applications, they
are severely limited because they are unable to provide the
1. Introduction range of operating system functionality that many legacy ap-
plications demand.
Web browsers make it straightforward to build user interfaces,
To overcome these limitations, we introduce B ROWSIX,
but they can be difficult to use as a platform to build sophis-
a framework that brings Unix abstractions to the browser
ticated applications. Code must generally be written from
through a shared kernel and common system-call conventions,
scratch or heavily modified; compiling existing code or li-
bridging the gap between conventional operating systems and
braries to JavaScript is not sufficient because these applica-
the browser. B ROWSIX consists of two core components: (1) a
tions depend on standard OS abstractions like processes and a
JavaScript-only operating system that exposes a wide array of
shared file system, which browsers do not support. Many web
OS services that applications expect (including pipes, concur-
applications are thus divided between a browser front-end and
rent processes, signals, sockets, and a shared file system); and
a server backend. The server runs on a traditional operating
(2) extended JavaScript runtimes for C, C++, Go, and Node.js
system, where the application can take advantage of familiar
OS abstractions and run a wide variety of off-the-shelf libraries 1 https://www.sharelatex.com/

and programs. 2 https://www.overleaf.com/


and pipes their standard output and standard error to the ap-
plication. These TEX programs use B ROWSIXs shared file
system to read in the users source files, and any packages,
class files, and fonts referenced within, as in a traditional Unix
environment. The filesystem transparently loads any needed
external packages from the TeX Live distribution over HTTP
upon first access. Subsequent accesses to the same files are in-
stantaneous, as the browser caches them. While a full TeX Live
distribution is several gigabytes in size, a typical paper only
needs to retrieve several megabytes worth of packages before
it can be built. If the user cancels PDF generation, B ROWSIX
Figure 1: A LATEX editor built using B ROWSIX. B ROWSIXs OS sends a SIGKILL signal to these processes. If PDF generation
services and language runtimes make it possible to run com- fails, the application can display the captured standard out
plex legacy code (including pdflatex and bibtex) directly and standard error. The result is serverless PDF generation
in the browser, without code modifications (See Section 2 for composed from off-the-shelf parts, with minimal engineering
details.) effort required to glue them together.
We demonstrate the utility of B ROWSIX with two further
that let unmodified programs written in these languages and case studies. Using B ROWSIX, we build an application that dy-
compiled to JavaScript run directly in the browser. Because namically routes requests to a remote server or an in-B ROWSIX
B ROWSIXs components are written entirely in JavaScript and server, both compiled from the same source code, depending
require no plugins, applications using B ROWSIX can run in a on the clients performance and battery characteristics. We also
wide range of modern web browsers including Google Chrome, use B ROWSIX to build a UNIX terminal exposing a POSIX
Mozilla Firefox, and Microsoft Edge. B ROWSIX makes it pos- shell, enabling developers to launch and compose applications
sible to port a wide range of existing applications and their and inspect B ROWSIX state in a familiar way.
language runtimes to the browser by providing the core func-
tionality of a full operating system: Contributions
Processes: B ROWSIX implements a range of process related This paper makes the following contributions:
system calls (including fork, spawn, exec, and wait4) and Bringing OS Abstractions and Services to the Browser.
provides a process primitive on top of Web Workers, letting We demonstrate that it is possible to provide a wide range
applications run in parallel and spawn subprocesses. of key Unix abstractions and services in the browser on top
Signals: B ROWSIX supports a substantial subset of the of existing web APIs. We implement these in B ROWSIX, a
POSIX signals API, including kill and signal handlers, JavaScript-only framework featuring a kernel and system
letting processes communicate with each other asyn- calls that runs on all modern browsers (3).
chronously. Runtime Integration for Existing Languages. We ex-
Shared Filesystem: B ROWSIX lets processes share state tend the JavaScript runtimes of Emscripten (a C/C++ to
through a shared FS. JavaScript toolchain), GopherJS (a Go to JavaScript com-
Pipes: B ROWSIX exposes the standard pipe API, making it piler), and Node.js with B ROWSIX support, letting unmodi-
simple for developers to compose processes into pipelines. fied C, C++, Go, and Node.js programs execute and inter-
Sockets: B ROWSIX supports TCP socket servers and clients, operate with one another within the browser as B ROWSIX
making it possible to run server applications like databases processes (4).
and HTTP servers together with their clients in the browser. Case Studies. We demonstrate B ROWSIXs utility by build-
Language Agnostic: B ROWSIX includes integration with ing a LATEX editor, a serverless client-server web application,
the runtime libraries of Emscripten (C/C++), GopherJS and a Unix terminal out of off-the-shelf components without
(Go), and Node.js (JavaScript) to allow unmodified applica- modification. We characterize B ROWSIXs performance un-
tions written in these languages to run directly as processes der these case studies and with microbenchmarks (5) and
in the browser. Through its simple system call API, develop- show that its overhead is low enough for real-world usage.
ers can straightforwardly integrate B ROWSIX into additional Guidance for Future Browsers. Based on our experience
language runtimes. writing B ROWSIX, we discuss current browser limitations
and propose solutions (6).
B ROWSIX dramatically simplifies the process of porting
complex applications to the browser environment. As a demon- 2. B ROWSIX Overview
stration, we have built a LATEX editor that runs entirely within
the browser. When the user requests a PDF, B ROWSIX runs To give an overview of B ROWSIXs features, this section walks
make to re-build the document with pdflatex and bibtex, through the process of using B ROWSIX to build an in-browser

2
LATEX editor using TeX Live utilities and GNU Make. Figure 1 requires this setting.
displays a screenshot of the editor. From this point, the build process is mostly unchanged from
a standard compilation. For programs that use the autotools
2.1. LATEX Editor Overview build system, such as GNU Make and TeX Live, instead of
The editor presents a split-screen view to the user, with the doc- running ./configure, the developer invokes emconfigure
uments LATEX source on the left, and generated PDF preview ./configure. This wrapper overrides standard tools like cc
on the right. The editors UI is a standard web application, and ar with em prefixed alternatives that compile the program
and represents the only new code. When the user clicks on the with Emscripten, which produces individual JavaScript files
Build PDF button, the editor uses B ROWSIX to invoke GNU for each program.
Make in a B ROWSIX process, which rebuilds the PDF. Staging the Filesystem: Next, the developer configures
The process for building the PDF is familiar to anyone who B ROWSIXs in-browser filesystem so that it hosts all of the
has used LATEX, except B ROWSIX performs all of the needed files that the programs require. B ROWSIXs file system ex-
steps entirely in the browser instead of server-side. It runs tends Doppios BrowserFS file system with multi-process sup-
GNU Make to read a Makefile from B ROWSIXs file system, port, building on its support for files backed by cloud storage,
which contains rules for rebuilding LATEX projects. Make then browser-local storage, traditional HTTP servers, and more [9].
runs pdflatex and bibtex, depending on whether the user For our LATEX example, both pdflatex and bibtex require
has updated the references file. read access to class, font, and other files from a LATEX distribu-
pdflatex and bibtex read any required LATEX packages, tion to function properly. While a complete TeX Live distribu-
fonts, and other system files from B ROWSIXs file system, tion contains over 60,000 individual files, the average LATEX
which lazily pulls in files as needed from the network. Both of document only references a small subset of these files.
these applications write their output to B ROWSIXs file system. To reduce load times and minimize the amount of stor-
Once all steps have completed (or an error has occurred), the age required on a clients device, the developer can leverage
Make process exits with an exit code indicating whether or not B ROWSIXs filesystem to load only the needed files. In this
PDF generation succeeded. B ROWSIX sends the exit code back case, the developer uploads a full TeX Live distribution to an
to the web application. If GNU Make exits normally, the editor HTTP server and configures B ROWSIXs filesystem to use an
reads the PDF from B ROWSIXs shared filesystem and displays HTTP-backed filesystem backend. The filesystem will then
it to the user. Otherwise, it displays the standard output from load these files on-demand from the network upon first ac-
pdflatex and bibtex to the user, which describes the source cess. The browser caches these files automatically, making
of the error. subsequent access much faster.
2.2. Building with B ROWSIX B ROWSIX Setup Code: Finally, the developer adds code to
the web application to load and initialize B ROWSIX, and to
Building any web application that runs Unix programs in launch make to build the PDF. A script tag in the HTML
B ROWSIX generally consists of the same three step process: loads browsix.js, and a subsequent script tag with inline
(1) compile the programs to JavaScript (using tools with JavaScript calls B ROWSIXs Boot function with the desired
B ROWSIX support), (2) stage files required by the applica- filesystem configuration.
tion for placement in the in-browser filesystem, and (3) add Additional application-specific initialization follows as
setup code to the web application to initiate B ROWSIX and usual. Once the filesystem is ready, the developer adds
launch the programs. code to read the contents of main.tex and main.bib from
Compiling to JavaScript: To run pdflatex, bibtex, and B ROWSIXs filesystem, and display the contents in the editor;
GNU Make in B ROWSIX, the developer compiles each pro- The application then registers a callback function with the
gram to JavaScript using Emscripten, a C/C++ to JavaScript Build PDF button to be run whenever the user clicks the
compiler [12]. We extend Emscriptens runtime library with button.
B ROWSIX support, so standard Unix APIs map to B ROWSIX
2.3. Execution with B ROWSIX
primitives. We discuss this extension in more detail in Sec-
tion 4.3. When the applications callback is executed in response to a
Before compilation, the developer needs to determine if any users Build PDF click, the application invokes the system
of the programs use the fork command. Due to browser limi- method on its kernel instance to start make. Make runs
tations explored in Section 6, B ROWSIX can only implement pdflatex and bibtex as described in Section 1. When the
fork as an asynchronous system call, which requires the use application receives a notification from B ROWSIX that Make
of a variant of Emscriptens compilation procedure that gener- has exited, it inspects Makes exit code. If it is zero, the PDF
ates less efficient code. If this option is configured incorrectly, was generated successfully and is read from the filesystem.
the program will fail at runtime when it attempts to invoke Otherwise, the captured standard output and standard error are
fork. For the LATEX example, only GNU Make uses fork and displayed to the user so they can debug their markup.

3
Component Lines of Code (LoC) Class System calls
Kernel 2,249 Process Management fork, spawn, pipe2,
BrowserFS modifications 1,231 wait4, exit
Shared syscall module 421 Process Metadata chdir, getcwd, getpid
Emscripten integration* 1,557
(C/C++ support) Sockets socket, bind, getsockname,

GopherJS integration* 926 listen, accept, connect

(Go support) Directory IO readdir, getdents,


Node.js integration* 1,742 rmdir, mkdir
TOTAL 8,126 File IO open, close, unlink,
Figure 2: B ROWSIX components. * indicates these components llseek, pread, pwrite
are written in JavaScript, while the rest of the components are File Metadata access, fstat, lstat,
written in TypeScript (which compiles to JavaScript). stat, readlink, utimes

This overview demonstrates how straightforward B ROWSIX Figure 3: A representative list of the system calls implemented
makes it to run existing components designed to work in a by the B ROWSIX kernel. fork is only supported for C and C++
Unix environment and execute them seamlessly inside a web programs.
browser. The next two sections provide technical details on 3.2. System Calls
how B ROWSIX provides Unix-like abstractions in the browser
environment and integrates with language runtimes. The B ROWSIX kernel supports two types of system calls: asyn-
chronous and synchronous. Asynchronous system calls work
3. B ROWSIX OS Support in all modern browsers, but impose a high performance penalty
on C and C++ programs. Synchronous system calls enable C
The core of B ROWSIXs OS support is a kernel that controls and C++ programs to perform much better, but currently only
access to shared Unix services. Unix services, including the work in Google Chrome via a mechanism we describe below;
shared file system, pipes, sockets, and task structures, live in- this mechanism is already standardized and is on track to be
side the kernel, which runs in the main browser thread. Pro- supported by other mainstream browsers.
cesses run separately and in parallel inside Web Workers, and
Asynchronous System Calls: B ROWSIX implements asyn-
access B ROWSIX kernel services through a system call inter-
chronous system calls in a continuation-passing style (CPS).
face. B ROWSIX and all of its runtime services are implemented
A process initiates a system call by sending a message to the
in JavaScript and TypeScript, a typed variant of JavaScript that
kernel with a process-specific unique ID, the system call num-
compiles to pure JavaScript. Figure 2 provides a breakdown
ber, and arguments. B ROWSIX copies all arguments, such as
of each of B ROWSIXs components.
file descriptor or a buffer to write to a file descriptor, from
the process to the kernel - no memory is shared. When the
3.1. Kernel
kernel sends a response, the Web Worker process executes the
The kernel lives in the main JavaScript context alongside the continuation (or callback) with response values, also copied
web application, and acts as the intermediary between pro- from the kernels heap into the processs heap.
cesses and loosely coupled Unix subsystems. Processes issue Asynchronous system calls work well for Node.js and Go,
system calls to the kernel to access shared resources, and but are a poor match for many C and C++ programs. In Node.js,
the kernel relays these requests to the appropriate subsystem. filesystem and other APIs accept a callback parameter to in-
When the subsystem responds to the system call, it relays the voke when a response is ready, which matches B ROWSIXs
response to the process. The kernel is also responsible for asynchronous system call mechanism. The GopherJS runtime
dispatching signals to processes, which we describe further in provides support for suspending and resuming the call stack in
Section 3.3. Figure 3 presents a partial list of the system calls order to implement goroutines (lightweight thread-like prim-
that the kernel currently supports. itives); this support also meshes with B ROWSIXs approach.
However, when using Emscripten to compile C and C++ pro-
In a departure from modern Unix systems, B ROWSIX does
grams, they must be compiled in an interpreted mode (called
not support multiple users. A traditional kernel would, for
the Emterpreter) in order to use asynchronous system calls.
example, use user identities to check permissions on certain
This mode produces much less efficient code than the standard
system calls or for access to files. Instead, B ROWSIX lever-
compiler, which produces asm.js output by default.
ages and relies on the browsers built-in sandbox and security
features, such as the same origin policy. In other words, a Synchronous System Calls: Synchronous system calls
B ROWSIX application enjoys the same level of protection and work by sharing a view of a processs address space between
security as any other web application. the kernel and the process, similar to a traditional operating

4
system kernel like Linux. At startup, the language runtime in faces (notably excluding the Document Object Model (DOM)),
a process wishing to use synchronous system calls passes to runs in a separate execution context, and can only communi-
the kernel (via an asynchronous system call) a reference to the cate with the main browser context via asynchronous message
heap (a SharedArrayBuffer object), along with two offsets into passing. Web Workers are not aware of one another, cannot
the heap: where to put system call return values, and an offset share memory with one another, and can only exchange mes-
to use to wake the process when the syscall is complete. sages with the main browser context that created them (see
A process invokes a synchronous system call by sending Section 6 for a discussion). Major browsers like Chrome and
a message, as in the asynchronous case, but arguments are Safari do not support spawning sub-workers from workers,
just integers and integer offsets (representing pointers) into so-called nested workers, and have not added support for them
the shared memory array, rather than larger objects (like Ar- since they were first proposed in 2009. Thus, if a Web Worker
rayBuffers) that would need to be copied between heaps. For needs to perform a task in parallel, it must delegate the request
system calls like pread, data is copied directly from the filesys- to the main browser thread, and proxy all messages to that
tem, pipe or socket into the processs heap, avoiding a poten- worker through the main browser thread. Perhaps unsurpris-
tially large allocation and extra copy. ingly, the limitations and complexity of Web Workers have
After sending a message to the kernel, the process per- hindered their adoption in web applications.
forms a blocking wait on the address previously arranged By contrast, B ROWSIX implements Unix processes on top of
with the kernel and is awakened when the system call has Web Workers, giving developers a familiar and full-featured ab-
completed or a signal is received. This wait is provided by the straction for parallel processing in the browser. Each B ROWSIX
JavaScript Atomics.wait function, part of the ECMAScript process has an associated task structure that lives in the kernel
Shared Memory and Atomics specification [4]. A side effect of that contains its process ID, parents process ID, Web Worker
this approach is that fork is not compatible with synchronous object, current working directory, and map of open file descrip-
system calls, as there is no way to re-wind or jump to a partic- tors. Processes have access to the system calls in Figure 3,
ular call stack in the child Web Worker. and invoke them by sending a message with the system call
Synchronous system calls are faster in practice for a number name and arguments to the kernel. As a result, processes can
of reasons. First, they only require one message to be passed share state via the file system, send signals to one another,
between the kernel and process, which is a relatively slow spawn sub-processes to perform tasks in parallel, and con-
operation. Second, system call arguments are numbers, rather nect processes together using pipes. Below, we describe how
than potentially large arrays that need to be copied between B ROWSIX maps familiar OS interfaces onto Web Workers.
JavaScript contexts. Finally, synchronous system calls provide spawn: B ROWSIX supports spawn, which constructs a new
a blocking primitive and do not depend on language runtimes process from a specified executable on the file system. spawn
to unwind and rewind the call stack. As such, they are suitable is the primary process creation primitive exposed in modern
for use with asm.js and WebAssembly functions on the call programming environments such as Go and Node.js, as fork
stack, which are faster and more amenable to optimization by is unsuitable for general use in multithreaded processes. spawn
the JavaScript runtime than Emscriptens interpreter. lets a process specify an executable to run, the arguments to
Synchronous system calls currently require the in- pass to that executable, the new processs working directory,
development browser features SharedArrayBuffers and Atom- and the resources that the subprocess should inherit (such as
ics, and currently only work in Google Chrome when launched file descriptors). In B ROWSIX, executables include JavaScript
with extra flags. SharedArrayBuffers and Atomics are on the files, file beginning with a shebang line, and WebAssembly
standards track, and are expected to be supported un-flagged files. When a process invokes spawn, B ROWSIX creates a
in mainstream browsers in the near future. new task structure with the specified resources and working
3.3. Processes directory, and creates a new Web Worker that runs the target
executable or interpreter.
B ROWSIX relies on Web Workers as the foundation for emulat- There are two technical challenges to implementing spawn.
ing Unix processes. However, Web Workers differ substantially First, the Web Worker constructor takes a URL to a JavaScript
from Unix processes, and B ROWSIX must provide a significant file as its first argument. Files in B ROWSIXs file system
amount of functionality to bridge this gap. may not correspond to files on a web server. For example,
In Unix, processes execute in isolated virtual address spaces, they might be dynamically produced by other B ROWSIX pro-
run in parallel with one another when the system has multiple cesses. To work around this restriction, B ROWSIX generates a
CPU cores, and can interact with system resources and other JavaScript Blob object that contains the data in the file, obtains
processes via system calls. However, the web browser does a dynamically-created URL for the blob from the browsers
not expose a process API to web applications. Instead, web window object, and passes that URL as a parameter to the Web
applications can spawn a Web Worker that runs a JavaScript Worker constructor. All modern web browsers now support
file in parallel with the application. constructing Workers from blob URLs.
A Web Worker has access to only a subset of browser inter- The second challenge is that there is no way to pass data to a

5
Worker on startup apart from sending a message. As processes 3.5. Sockets
synchronously access state like the arguments vector and en-
vironment map, B ROWSIX-enabled runtimes delay execution B ROWSIX implements a subset of the BSD/POSIX socket
of a processs main() function until after the worker has re- API, with support for SOCK_STREAM (TCP) sockets for com-
ceived an init message containing the processs arguments municating between B ROWSIX processes. These sockets en-
and environment. able servers that bind, listen and then accept new connec-
tions on a socket, along with clients that connect to a socket
fork: The fork system call creates a new process contain- server, with both client and server reading and writing from
ing a copy of the current address space and call stack. Fork the connected file descriptor. Sockets are sequenced, reliable,
returns twice first with a value of zero in the new process, and bi-directional streams.
with the PID of the new process in the original. Web Workers
3.6. Shared File System
do not expose a cloning API, and JavaScript lacks the reflec-
tion primitives required to serialize a contexts entire state into B ROWSIX builds on and significantly extends BrowserFSs file
a snapshot. Thus, B ROWSIX only supports fork when a lan- system, part of Doppio [9]. BrowserFS already included sup-
guage runtime is able to completely enumerate and serialize port for multiple mounted filesystems in a single hierarchical
its own state. Section 4 describes how we extend Emscripten directory structure. BrowserFS provides multiple file system
to provide fork support for C/C++ programs compiled to backend implementations, such as in-memory, zip file, XML-
JavaScript. HttpRequest, Dropbox, and an overlay filesystem. BrowserFS
provides a unified, encapsulated interface to all of these back-
wait4: The wait4 system call reaps child processes that ends.
have finished executing. It returns immediately if the specified B ROWSIX extends BrowserFS in two key ways: it adds
child has already exited, or the WNOHANG option is specified. multi-process support and incorporates improved support for
Waiting requires that the kernel not immediately free task struc- loading files over HTTP. To provide multi-process support,
tures, and required us to implement the zombie task state for B ROWSIXs file system adds locking operations to the over-
children that have not yet been waited upon. The C library used lay filesystem to prevent operations from different processes
by Emscripten, musl, uses the wait4 system call to implement from interleaving. In addition, B ROWSIX incorporates domain-
the C library functions wait, wait3, and waitpid. specific optimizations into its file system; for example, it
avoids expensive operations like recording the call stack when
exit: Language runtimes with B ROWSIX-support are re- a path lookup fails (a common event).
quired to explicitly issue an exit system call when they are
B ROWSIX modifies BrowserFSs overlay backend to lazily
done executing, as the containing Web Worker context has
load files from its read-only underlay; the original version
no way to know that the process has finished. This is due to
eagerly read all files from the read-only filesystem upon initial-
the event-based nature of JavaScript environments even if
ization. B ROWSIXs approach drastically improves the startup
there are no pending events in the Workers queue, the main
time of the kernel, minimizes the amount of data transferred
JavaScript context could, from the perspective of the browser,
over the network, and enables applications like the LATEX edi-
send the Worker a message at any time.
tor where only a small subset of files are required for a given
end user.
getpid, getppid, getcwd, chdir: These four system calls
Finally, B ROWSIX implements system calls that operate on
operate on the data in current processs task structure, which
paths, like open and stat, as method calls to the kernels
lives in the B ROWSIX kernel. getpid returns the processs ID,
BrowserFS instance. When a system call takes a file descriptor
getppid returns the parent processs ID, getcwd returns the
as an argument, the kernel looks up the descriptor in the taskss
processs working directory, and chdir changes the processs
file hashmap and invokes the appropriate method on that file
working directory.
object, calling into BrowserFS for regular files and directories.
Child processes inherit file descriptor tables, and B ROWSIX
3.4. Pipes
manages each object (whether it is a file, directory, pipe or
socket) with reference counting.
B ROWSIX pipes are implemented as in-memory buffers with
read-side wait queues. If there is no data to be read when a 4. B ROWSIX Runtime Support
process issues a read system call, B ROWSIX enqueues the
callback encapsulating the system call response which it in- Applications access B ROWSIX system calls indirectly through
vokes when data is written to the pipe. Similarly, if there is their runtime systems. This section describes the runtime sup-
not enough space in a pipes internal buffer, B ROWSIX only port we added to GopherJS, Emscripten, and Node.js along
invokes the callback encapsulating the system call response to with the APIs exposed to web applications so they can execute
the write operation when the pipe is read from. programs in B ROWSIX.

6
kernel.system( function sys_getdents64(cb, trap, fd, dirp, len) {
pdflatex example.tex, var done = function (err, buf) {
function(pid, code) { if (!err)
if (code === 0) { dirp.set(buf);
displayPDF(); cb([err ? -1 : buf.byteLength, 0, err ? err : 0]);
} else { };
displayLatexLog(); syscall_1.syscall.getdents(fd, len, done);
} }
}, logStdout, logStderr);

Figure 5: Implementing the getdents64 system call in Go-


Figure 4: Creating a B ROWSIX process from JavaScript. pherJS.
4.1. Browser Environment Extensions system calls. The common syscall module provides a way
Web applications run alongside the B ROWSIX kernel in the to register signal handlers for the standard Unix signals, such
main browser context, and have access to B ROWSIX features as SIGCHLD.
through several global APIs. B ROWSIX exposes new APIs 4.3. Runtime-specific Integration
for process creation, file access, and socket notifications, and
an XMLHttpRequest-like interface to send HTTP requests to For many programming languages, existing language run-
B ROWSIX processes. times targeted for the browser must bridge the impedance
File access acts as expected, and allows the client to ma- mismatch between synchronous APIs present on Unix-like
nipulate the filesystem, invoke a utility or pipeline of utilities, systems and the asynchronous world of the browser. Compile-
and read state from the filesystem after programs have fin- to-JavaScript systems like Emscripten, ClojureScript [6],
ished executing. Figure 4 shows how client applications invoke Scala.js [2], js_of_ocaml [10], WhaleSong (Racket) [11], and
B ROWSIX processes and react when processes exit through an GopherJS all employ different approaches. Since B ROWSIX
API similar to Cs system. supports both synchronous and asynchronous system calls,
Socket notifications let applications register a callback to language runtimes can choose the system call convention most
be invoked when a process has started listening on a particular appropriate for their implementation.
port. These notifications let web applications launch a server This section describes the runtime support we added to
as a process and appropriately delay communicating with the language runtimes for Go, C/C++, and Node.js. Extending
server until it is listening for messages. Web applications do B ROWSIX support to additional language runtimes remains as
not need to resort to polling or ad hoc waiting. future work.
B ROWSIX provides an XMLHttpRequest-like API for send- Go: Go is a systems language developed at Google designed
ing requests from the web application to in-browser HTTP for readability, concurrency, and efficiency. To run Go pro-
servers running in B ROWSIX. This allows JavaScript to inter- grams under B ROWSIX, we extended the existing GopherJS
act with HTTP 1.1 servers running as B ROWSIX processes as compiler and runtime to support issuing and waiting for sys-
if they were remote HTTP servers. The API encapsulates the tem calls under B ROWSIX. GopherJS already provides full
details of connecting a B ROWSIX socket to the server, serializ- support for Go language features like goroutines (lightweight
ing the HTTP request to a byte array, sending the byte array to threads), channels (communication primitives), and delayed
the B ROWSIX process, processing the (potentially chunked) functions.
HTTP response, and generating the expected web events. We extended the GopherJS runtime with support for
4.2. Common Services B ROWSIX through modifications to the runtime. The main
integration points are a B ROWSIX-specific implementation of
B ROWSIX provides a small syscall layer as a JavaScript the syscall.RawSyscall function (which handles syscalls
module that runs in a Web Worker. This layer provides a in Go), along with overrides of several Go runtime functions.
concrete, typed API for asynchronous system calls over the The replacement for RawSyscall is implemented in Go. It
browsers message passing primitives. Language runtimes use allocates a Go channel object, and this function invokes the
this module to communicate with the shared kernel. Methods B ROWSIX JavaScript syscall library, passing the system call
provided by the syscall layer take the same arguments as number, arguments, and a callback to invoke. RawSyscall
Linux system calls of the same name, along with an additional then performs a blocking read on the Go channel, which sus-
argument: a callback function. This callback is executed when pends the current goroutine until the callback is invoked. When
the syscall module receives a message response from the ker- the system call response is received from the B ROWSIX ker-
nel. Unlike a traditional single-threaded process, a B ROWSIX nel, GopherJSs existing runtime takes care of re-winding the
process can have multiple outstanding system calls, which en- stack and continuing execution. The syscall library invokes
ables runtimes like GopherJS to implement user-space threads a function specific to each supported system call to marshal
on top of a single Web Worker execution context. data to and from the B ROWSIX kernel. Adding support for any
Signals are sent over the same message passing interface as new system call is a matter of writing a small handler function

7
__syscall220: function(which, varargs) { After the kernel launches a new Web Worker, it transfers this
#if EMTERPRETIFY_ASYNC copy of global memory and PC to the new Worker as part of
return EmterpreterAsync.handle(function(resume) {
var fd = SYSCALLS.get(), dirp = SYSCALLS.get(), count = the initialization message. When the Emscripten runtime in
SYSCALLS.get(); the new B ROWSIX process receives the initialization message,
var done = function(err, buf) {
if (err > 0) if a memory array and PC are present the runtime swaps them
HEAPU8.subarray(dirp, dirp+buf.byteLength).set(buf); in and invokes the Emterpreter to continue from where fork
resume(function() {
return err; was invoked.
});
}; Node.js: Node.js (a.k.a. Node) is a platform for building
SYSCALLS.browsix.syscall.async(done, getdents, [fd,
count]);
servers and command line tools with JavaScript, implemented
}); in C, C++ and JavaScript, on top of the v8 JavaScript en-
#else
var fd = SYSCALLS.get(), dirp = SYSCALLS.get(), count =
gine. Node.js APIs are JavaScript modules that can be loaded
SYSCALLS.get(); into the current browser context by invoking the require
return SYSCALLS.browsix.syscall.sync(220, fd, dirp, count);
#endif
built-in function. These high-level APIs are implemented in
}, platform-agnostic JavaScript, and call into lower-level C++
bindings, which in turn invoke operating system interfaces like
Figure 6: Implementing the B ROWSIX getdents64 syscall in filesystem IO, TCP sockets, and child process management.
Emscripten. Node.js embraces the asynchronous, callback-oriented nature
and registering it; an example is shown in Figure 5 of JavaScript most Node APIs that invoke system calls take
a callback parameter that is invoked when results are ready.
B ROWSIX replaces a number of low-level runtime functions;
the most important are syscall.forkAndExecInChild and To run servers and utilities written for Node.js under
net.Listen. The former is overridden to directly invoke
B ROWSIX, we provide a browser-node executable that pack-
B ROWSIXs spawn system call, and the latter to provide access ages Nodes high-level APIs with pure-JavaScript replace-
to B ROWSIX socket services. Additional integration points in- ments for Nodes C++ bindings that invoke B ROWSIX system
clude an explicit call to the exit system call when the main calls as a single file that runs in a B ROWSIX process. B ROWSIX
function exits, and waiting until the processs arguments and also replaces several other native modules, like the module
environment have been received before starting main() (see for parsing and generating HTTP responses and requests, with
3.3). pure JavaScript implementations. Node executables can be in-
voked directly, such as node server.js, or will be invoked
C and C++: We also extend Emscripten, Mozilla Researchs indirectly by the kernel if node is specified as the interpreter
LLVM-based C and C++ compiler that targets JavaScript, with in the shebang line of a text file marked as executable.
support for B ROWSIX. B ROWSIX-enhanced Emscripten sup-
ports two modes - synchronous system calls and asynchronous 5. Evaluation
system calls (described in Section 3.2), one of which is se-
lected at compile time. When asynchronous system calls are Our evaluation answers the following questions: (1) Does
used, it requires use of Emscriptens interpreter mode (named bringing Unix abstractions into the browser enable compelling
the Emterpreter) to save and restore the C stack. B ROWSIXs use cases? (2) Is the performance impact of running programs
asynchronous system calls require all functions that may be on under B ROWSIX acceptable?
the stack under a system call to be interpreted so that the stack 5.1. Case Studies
can be restored when the system call completes. Emscripten
can selectively compile other parts of an application to asm.js, We evaluate the applicability and advantages of bringing Unix
which will be JIT-compiled and run as native JavaScript by the abstractions into the browser with two case studies, in addition
browser. Synchronous system calls do not have this limitation. to the LATEX editor from the overview (2). First, we build a
As with GopherJS, Emscripten provides a clear integration web application for creating memes that can run its unmodi-
point at the level of system calls. Emscripten provides im- fied server in B ROWSIX. The meme generator transparently
plementations for a number of system calls, but is restricted switches between generating memes in-browser or server-side
to performing in-memory operations that do not block. We depending on network and device characteristics. Second, we
replace Emscripten system call implementations with ones build a Unix terminal that lets application developers use dash,
that call into the B ROWSIX kernel, such as in Figure 6. In the a widely-used POSIX shell, to interact with B ROWSIX in a
case of getdents and stat, padding was added to C struc- familiar manner.
ture definitions to match the layout expected by the B ROWSIX 5.1.1. Meme Generator: Our meme generator lets users cre-
kernel. ate memes consisting of images with (nominally) humorous
When a C process invokes fork, the runtime sends a copy of overlaid text. Figure 7 contains a screenshot. Existing ser-
the global memory array, which includes the C stack and heap, vices, such as MemeGenerator.net, perform meme gener-
along with the current program counter (PC) to the kernel. ation server-side. Moving meme creation into the browser

8
5.1.2. The B ROWSIX Terminal: To make it easy for devel-
opers to interact with and test programs in B ROWSIX, we
implement an in-browser Unix terminal that exposes a POSIX
shell. The terminal uses the Debian Almquist shell (dash),
the default shell of Debian and Ubuntu. We compile dash to
JavaScript using B ROWSIX-enhanced Emscripten, and run it
in a B ROWSIX process.
Since the B ROWSIX terminal uses a standard Linux
shell, developers can use it to run arbitrary shell com-
mands in B ROWSIX. Developers can pipe programs together
(e.g. cat file.txt | grep apple > apples.txt), exe-
Figure 7: A meme generator built using B ROWSIX. All server-
cute programs in a subshell in the background with &, run
side functionality was moved into the browser without modify-
shell scripts, and change environment variables. Developers
ing any code.
can also run Go, C/C++, and Node.js programs.
would reduce server load and reduce latency when the net- The terminal includes a variety of Unix utilities on the
work is overloaded or unreliable, but doing so would normally shells PATH that we wrote for Node.js: cat, cp, curl, echo,
present a significant engineering challenge. The meme gen- exec, grep, head, ls, mkdir, rm, rmdir, sh, sha1sum, sort,
eration server uses sockets to communicate with the browser stat, tail, tee, touch, wc, and xargs. These programs run
over HTTP and reads meme templates from the file system. equivalently under Node and B ROWSIX without any modifi-
Before B ROWSIX, the client and server code would need to be cations, and were used heavily during development to debug
re-architected and rewritten to run together in the browser. B ROWSIX functionality.
To demonstrate B ROWSIXs ability to quickly port code Summary: B ROWSIX makes it trivial to execute applica-
from the server to the web, we implement our meme creator as tions designed to run in a Unix environment within the browser,
a traditional client/server web application; Figure 8a contains enabling the rapid development of sophisticated web applica-
a system diagram. The client is implemented in HTML5 and tions. These applications can incorporate server code into the
JavaScript, and the server is written in Go. The server reads browser and harness the functionality of existing applications.
base images and font files from the filesystem, and uses off-
the-shelf third-party Go libraries for image manipulation and 5.2. Performance
font rendering to produce memes [3]. The server also uses We evaluate the performance overhead of B ROWSIX on our
Gos built-in http module to run its web server. Note that case studies. All experiments were performed on a Thinkpad
this server is stateless, following best practices [7]; porting a X1 Carbon with an Intel i7-5600U CPU and 8 GB of RAM,
stateful server would naturally require more care. running Linux 4.7.0.
To port the server to B ROWSIX, we follow the process out-
lined in Section 2. First, we compile the Go server to a single LATEX Editor: Running pdflatex under B ROWSIX im-
JavaScript file using GopherJS, a Go to JavaScript compiler [8]. poses an order of magnitude slowdown. A native build of
Then, we stage the font and images for the BrowserFS filesys- pdflatex under Linux takes around 100 milliseconds on a
tem. Finally, we augment the client application to load the single page document with a bibliography. When using syn-
B ROWSIX JavaScript module, initialize a kernel instance, and chronous calls (as supported by Google Chrome), the same
start the meme-server. document builds in B ROWSIX in just under 3 seconds. While
Next, we augment the web application to dynamically route in relative terms this is a significant slowdown, this time is
meme generation requests to a server running in B ROWSIX fast enough to be acceptable. Using asynchronous system
or to the cloud. We add a function to the application that calls and the Emterpreter, which is only necessary to enable
implements a simple policy: if the network is inaccessible, broader compatibility with todays browsers, increases runtime
or the browser is running on a desktop (which is a proxy for to around 12 seconds.
a powerful device), the application routes meme generation Meme Generator: The meme generator performs two types
requests to the server running in B ROWSIX. Otherwise, it of HTTP requests to the server: requests for a list of available
sends the requests to the remote server. In both cases, the web background images, and requests to generate a meme. We
application uses an XMLHttpRequest-like interface to make benchmark the performance of the meme generator server run-
the request, requiring little change to the existing code. ning natively and running in Browsix in both Google Chrome
Figure 8b displays a system diagram of the modified meme and Firefox.
generator. With this modification, meme generation works On average, a request for a list of background images takes
even when offline. The code required to implement this pol- 1.7 milliseconds natively, 9 ms in Google Chrome, and 6 ms
icy and dynamic behavior amounted to less than 30 lines of in Firefox. While requests to a server running natively on
JavaScript. the same machine as the client are faster than those served

9
(a) Meme creator running without B ROWSIX

(b) Meme creator running with B ROWSIX

Figure 8: System diagram of the meme generator application with and without B ROWSIX, demonstrating how the client and server
interact with one another. With B ROWSIX, the server runs in the browser without code modifications.

by B ROWSIX, B ROWSIX is faster once a network connection Command Native Node.js B ROWSIX
and roundtrip latencies are factored in. When comparing an sha1sum 0.002s 0.067s 0.189s
instance of the meme-server running on an EC2 instance, the ls 0.001s 0.044s 0.108s
in-B ROWSIX request completed three times as fast as the re- Figure 9: Execution time of utilities under B ROWSIX, com-
quest to the remote machine. Times reported are the mean of pared to the same utilities run under Node.js, and the na-
100 runs following a 20-run warmup. tive GNU/Linux utilities. sha1sum is run on usr/bin/node,
The performance of meme generation is degraded by limi- and ls is run on /usr/bin. Running in JavaScript (with
tations in current browsers. The in-B ROWSIX HTTP request Node.js and B ROWSIX) imposes most overhead; running in the
takes approximately two seconds to generate a meme in the B ROWSIX environment adds roughly another 3 overhead.
browser, versus 200 ms when running server-side. This inef-
ficiency is primarily due to missing 64-bit integer primitives Summary: While B ROWSIXs performance is limited by the
when numerical code is compiled to JavaScript with GopherJS, performance of underlying browser primitives (notably, the
rather than overhead introduced by B ROWSIX; we expect this lack of native 64-bit longs), it provides acceptable performance
to improve with both when future browsers support native for a range of applications.
access to 64-bit integers, and with independent improvements
to the GopherJS compiler. 6. Discussion
The process of implementing B ROWSIX has highlighted op-
B ROWSIX Terminal and Utilities: Unix utilities provide
portunities for improvement in the implementation and speci-
a mechanism to compare the performance of real-world pro-
fication of browser APIs, especially Web Workers. We outline
grams under Linux and B ROWSIX. Figure 9 shows the re-
a number of optimizations and natural extensions that are gen-
sults of running the same JavaScript utility under B ROWSIX
erally useful, and would extend B ROWSIXs reach.
and on Linux under Node.js, and compares this to the execu-
tion time of the corresponding GNU Coreutils utility (written Worker Priority Control: The parent of a Web Worker
in C, running on Linux). Most of the overhead can be at- has no way to lower the priority of a created worker. As
tributed to JavaScript (the basis of Node.js and B ROWSIX); workers are implemented on top of OS threads, this con-
subsequently running in the B ROWSIX environment imposes cept maps cleanly onto OS-level priorities/niceness. Providing
roughly a 3 overhead over Node.js on Linux. Nonetheless, this facility would let web applications prevent a low-priority
this performance (completion in under 200 milliseconds) is CPU-intensive worker from interfering with the main browser
low enough that it should be generally acceptable to users. thread.

10
rs
ts

rve
lien
em

s
t se

sse
tc
t
sys

ls
cke

cke

oce

na
es
e

Pip

Sig
Fil

So

So

Pr
E NVIRONMENTS B ROWSIX 3 3 3 3 3 3
D OPPIO [9]
WebAssembly
L ANGUAGE RUNTIMES Emscripten (C/C++)
GopherJS (Go)
B ROWSIX + Emscripten 3 3 3 3 3 3
B ROWSIX + GopherJS 3 3 3 3 3 3
Table 1: Feature comparison of JavaScript execution environments and language runtimes for programs compiled to JavaScript.
indicates that the feature is only accessible by a single running process. B ROWSIX provides multi-process support for all of its
features.

postMessage() Backpressure: Traditional operating sys- 7. Related Work


tems attempt to prevent individual processes from affecting
In-Browser Execution Environments: B ROWSIX signif-
system stability in a number of ways. One of these is providing
icantly extends past efforts to bring traditional APIs and
backpressure, wherein the process attempting to write to a pipe
general-purpose languages to the browser; Table 1 provides
or socket is suspended (the system call blocks) until the other
a comparison. Doppios focus is providing single-process
end of the pipe reads the data, or it can fit into a fixed size
POSIX abstractions [9]. B ROWSIX builds on and extends its
buffer. This approach prevents unbounded resource allocation
filesystem component, BrowserFS, to support multiple pro-
in the kernel. In the browser, the postMessage() function
cesses. Emscripten compiles LLVM bytecode to JavaScript,
can be called from a JavaScript context an unbounded number
enabling the compilation of C and C++ to JavaScript [12];
of times and can eventually cause the browser to run out of
as Section 4 describes, B ROWSIX augments its runtime sys-
allocatable memory.
tem so that unmodified C and C++ programs compiled with
Message Passing Performance: Message passing is three Emscripten can take full advantage of its facilities. B ROWSIX
orders of magnitude slower than traditional system calls in the provides similar runtime support for Go programs through
two browsers we evaluate, Chrome and Firefox. A more effi- GopherJS [8].
cient message passing implementation would improve the per-
Kernel Design and OS Interfaces: B ROWSIX resembles a
formance of B ROWSIXs system calls and other inter-process
Linux kernel task running on a microkernel [5], as it relies
communication.
on an underlying system for messaging, scheduling and con-
Memory Mapping: B ROWSIX is currently unable to sup- text switching. Barrelfish, a many-core, heterogenous OS [1]
port C/C++ applications like PostgreSQL that use mmap. Em- showed that asynchronous, shared-nothing system calls could
scripten uses a single typed array to represent the unmanaged be practical. Additionally, B ROWSIX somewhat mirrors the
C/C++ heap. While recent browser interfaces make it possi- per-core, shared nothing structure of a multikernel, as individ-
ble to share this typed array among B ROWSIX processes [4], ual B ROWSIX processes do not use inter-domain communica-
browsers cannot yet map regions of the typed array into an- tion for tasks like memory allocation and timers.
other typed array, which would be necessary to fully emulate
mmap. Features on the WebAssembly roadmap, which aim for 8. Conclusion
implementation across browsers, would enable B ROWSIX to This paper introduces B ROWSIX, a framework that brings
support additional features like shared mmap and shm3 . the essence of Unix to the browser. B ROWSIX makes it al-
Stack Management: C provides the ability to save and most trivial to build complex web applications from com-
change the current threads context with the setcontext and ponents written in a variety of languages without modify-
getcontext functions. While rarely useful or advisable for ing any code, and promises to significantly reduce the ef-
applications, it enables specialized low-level libraries to save fort required to build highly sophisticated web applications.
and restore the C stack. A similar JavaScript primitive coupled B ROWSIX is open source, and is freely available at https:
with the use of SharedArrayBuffers would let B ROWSIX sup- //github.com/plasma-umass/Browsix.
port fork in Emscripten applications as a synchronous system
call.
3 https://github.com/WebAssembly/design/blob/master/

FutureFeatures.md

11
References
[1] A. Baumann, P. Barham, P.-E. Dagand, T. Harris, R. Isaacs, S. Peter,
T. Roscoe, A. Schpbach, and A. Singhania. The multikernel: a new
OS architecture for scalable multicore systems. In Proceedings of the
ACM SIGOPS 22nd symposium on Operating systems principles, pages
2944. ACM, 2009.
[2] S. Doeraene. Scala.js: Type-Directed Interoperability with Dynami-
cally Typed Languages. Technical report, cole polytechnique fdrale
de Lausanne, 2013.
[3] M. Fogleman. fogleman/gg: Go Graphics - 2D rendering in Go with a
simple API, 2016. https://github.com/fogleman/gg.
[4] L. T. Hansen and J. Fairbank. ECMAScript Shared Memory and Atom-
ics, 2016. https://tc39.github.io/ecmascript_sharedmem/
shmem.html.
[5] H. Hrtig, M. Hohmuth, J. Liedtke, J. Wolter, and S. Schnberg. The
performance of -kernel-based systems. In Proceedings of the Six-
teenth ACM Symposium on Operating Systems Principles, SOSP 97,
pages 6677, New York, NY, USA, 1997. ACM.
[6] R. Hickey. Clojurescript. http://clojurescript.org/about/
rationale, 2016.
[7] T. Mauro. Adopting Microservices at Netflix: Lessons for
Architectural Design, 2015. https://www.nginx.com/blog/
microservices-at-netflix-architectural-best-practices/.
[8] R. Musiol. gopherjs/gopherjs: A compiler from Go to JavaScript
for running Go code in a browser, 2016. https://github.com/
gopherjs/gopherjs.
[9] J. Vilk and E. D. Berger. D OPPIO: Breaking the browser language
barrier. In Proceedings of the 2014 ACM SIGPLAN Conference on
Programming Language Design and Implementation (PLDI 2014),
pages 508518. ACM, 2014.
[10] J. Vouillon and V. Balat. From bytecode to JavaScript: the Js_of_ocaml
compiler. Software: Practice and Experience, 44(8):951972, 2014.
[11] D. Yoo and S. Krishnamurthi. Whalesong: running racket in the
browser. In DLS13, Proceedings of the 9th Symposium on Dynamic
Languages, part of SPLASH 2013, Indianapolis, IN, USA, October
26-31, 2013, pages 97108, 2013.
[12] A. Zakai. Emscripten: an LLVM-to-JavaScript compiler. In OOPSLA
Companion, pages 301312, 2011.

12

You might also like