-
Notifications
You must be signed in to change notification settings - Fork 1
Why do we need system calls?
TL;DR The system functions give a way for the WASM code to call stuff outside of the WASM world, located into the runner program.
Let's think about access to a disk file.
Your program is called "foo" and it is compiled to WASM. How should it access a disk file called "bar"? In the WASM world where foo is running, there is no os.Open("bar") golang call because there is no "operating system" for os.Open() to talk to! In fact, if you tried to use that right now, it would probably compile ok, but it would crash when it tried to use that function at run-time. So, parigot has to provide some way for this to work.
In this case, there is be a file system
service that is a "mixed mode" service as explained previously. This service part that is implemented in WASM (that receives your call to "open a file") and a part that is in go to actually do some filesystem stuff. (Access to the disk is highly restricted and sandboxed.) Because the second part of the filesystem service is in go, it has have access to os.Open() and all the other filesystem calls in golang
that you would expect.
It will help you to know that WASM is just a specification of how a virtual "processor" will work. It does not define anything related to the environment that the processor runs in. So it defines the ADD instruction and the LOAD/STORE instructions but says nothing about to access a filesystem! That is the responsibility of what the WASM folks call the "container" that runs the virtual machine. In parigot, this is the "runner" program that provides these "outside the processor" capabilities. The only thing WASM does to help you in any way is that defines a CALL instruction that invokes a function--and it's possible to allow the container to implement a function itself.
So, system calls are really just like the example above with the file system service--using go (not WASM) to access capabilities that go provides that WASM does not... with one big exception. The system calls are the most basic example of this "using go to get extra capabilities" strategy and must be available in any programming language. In the example above the file system service is the only program that needs to communicate with the "go portion" of the filesystem code, so it can use any way of communicating it wants, any way of formatting the parameters, etc. A real system call cannot make this assumption.
So lets consider Locate() which is the system call referred to previously. In a normal program (see example/vvv/storeclient/main.go) there are calls to Locate() that are wrapped by the generated code to make them nicer. Suppose you want to access a service called Fleazil that is defined in the file fleazil.proto. (Just like service Store in example/vvv/proto/business.pb) When you want to use that service in your WASM program, you would do
fleazilsvc, _ : LocateFleazil()Look around line 20 in example/vvv/storeclient/main.go to see this in a real program:
vinnysStore, err := vvv.LocateStore()
if err != nil {
lib.Exit(1)
}This call LocateFleazil is just a wrapper around the Locate system call, and it returns you an implementation of the interface Fleazil which is defined in the generated code, based on what is in fleazil.proto. Since parigot allows the Fleazil service either to be in the same WASM binary as your program or over the network, clearly some trickery has to be done so that the object actually returned to you works either case. In fact, different implementations are used in the two cases. The reason system calls are more complex is that this has to work in python as well:
fleazil_service = parigot_locate("Fleazil")This multi-language issue means that the protocol used to talk to Locate() inside parigot must be the same for all languages, a problem that "file open" in the filesystem service does not have. In addition, parigot doesn't want some bogus user program to be able to crash the kernel, so we have to be very careful in check the parameters, their sizes, etc. The go portion of the filesystem service doesn't have to do as much of this because it knows that its only "customer" is the WASM portion of the same service.
There are some folks working on something called WASI that gives you access to the usual unix functions like Open(), Read(), Write(), etc and has file descriptors, probably unix sockets, and the like. They are implementing a standard "container" to provide these usual unix features. It was a conscious decision to start out not supporting WASI so the model of parigot would be clean...
Everything is a service.
This doesn't mean that later, it might not be interesting to have a "service" that provided parigot if you really really want it, but for now I'm trying to something that will differentiate my platform.