489 lines
15 KiB
TeX
489 lines
15 KiB
TeX
|
|
\section{Writing A Sample Barrelfish Application}
|
|
|
|
Here we show how to write a simple program on Barrelfish. \textbf{XXX:} no
|
|
error handling yet. Refer to \texttt{usr/tests/idctest} for code with proper
|
|
error handling.
|
|
|
|
\subsection{A Simple Hello World Program}
|
|
|
|
The source for user domains is stored under \begin{verbatim}/usr\end{verbatim}.
|
|
|
|
A simple hello world domain consists of a source file, and a Hakefile. e.g.,
|
|
\begin{verbatim}
|
|
usr/myapp
|
|
myapp.c
|
|
Hakefile
|
|
\end{verbatim}
|
|
|
|
For a simple hello world app, the source is trivial: a simple printf in main
|
|
will suffice (note that for output printf either uses a debug syscall to print
|
|
to the serial output, or a proper serial server if one is available).
|
|
|
|
\begin{verbatim}
|
|
#include <stdio.h>
|
|
int main(void) {
|
|
printf("Hello World\n");
|
|
return 0;
|
|
}
|
|
\end{verbatim}
|
|
|
|
To build the domain, call hake.sh in the build directory. This will create a
|
|
Config.hs, Makefile, and symbolic\_targets.mk. You can add myapp in the
|
|
symbolic\_tagets, then run make to build everything. You can also explicitly ask
|
|
make to build the domain. e.g.:
|
|
\begin{verbatim}
|
|
make x86_64/sbin/myapp
|
|
\end{verbatim}
|
|
|
|
After all is built, the menu.lst needs to be modified to add the domain and have
|
|
it started. Run everything in a simulator using:
|
|
|
|
\begin{verbatim}
|
|
make sim
|
|
\end{verbatim}
|
|
|
|
\section{A Simple Hello World Program Using IDC}
|
|
|
|
In this section, we talk how we can write an application that uses Barrelfish
|
|
IDC mechanism to communicate with other applications. For purpose of this
|
|
section, we will develop simple \textbf{Hello World} client and server
|
|
application. The client running on core-0 will send an IDC message to a
|
|
server running on core-1. This message will contain a string with contents
|
|
\texttt{"Hello World"}.
|
|
|
|
Adding IDC (inter-dispatcher communication) is a bit more complicated. It
|
|
requires defining an interface in Flounder IDL, updating the interfaces
|
|
Hakefile, updating the domain's (myapp) Hakefile, and then adding initialization
|
|
code as well as appropriate callbacks to the source file.
|
|
|
|
\begin{figure}
|
|
\begin{center}
|
|
\includegraphics[width=0.8\columnwidth]{helloWorld2.pdf}
|
|
\end{center}
|
|
\caption{Hello world application overview}
|
|
\label{fig:helloWorld}
|
|
\end{figure}
|
|
|
|
\subsection{Interface}
|
|
|
|
In this subsection, we will create a new interface
|
|
for our use. We do this by creating a new interface file called
|
|
\texttt{hello.if} in the \texttt{if\\} directory. Following is simple interface
|
|
that will suffice for our requirements of sending single string as a message.
|
|
|
|
|
|
\verbatiminput{if/hello.if}
|
|
\begin{verbatim}
|
|
interface hello "Hello World interface" {
|
|
message hello_msg(string s);
|
|
};
|
|
\end{verbatim}
|
|
|
|
You can find out more about how to write such interfaces by referring
|
|
\href{http://www.barrelfish.org/TN-011-IDC.pdf}{IDC-Technote}.
|
|
|
|
You also need to modify the \texttt{if/Hakefile} to add this newly created
|
|
interface into the compilation process.
|
|
|
|
\begin{verbatim}
|
|
[ flounderGenDefs (options arch) f
|
|
| f <- [ "ahci_mgmt",
|
|
...
|
|
...
|
|
"hello"],
|
|
arch <- allArchitectures
|
|
] ++
|
|
\end{verbatim}
|
|
|
|
Adding the name of your interface here will make sure that the communication
|
|
stubs will be automatically generated for by the compilation process. These
|
|
can be found in following location in your build directory:
|
|
|
|
\begin{verbatim}
|
|
BUILD/ARCH/include/if/hello_defs.h
|
|
BUILD/ARCH/include/if/hello_lmp_defs.h
|
|
BUILD/ARCH/include/if/hello_ump_defs.h
|
|
BUILD/ARCH/include/if/hello_multihop_defs.h
|
|
\end{verbatim}
|
|
|
|
There will be one for each type of communication mechanism supported by
|
|
the Barrelfish. You don't need to worry about these files as build mechanism
|
|
will take care of these files. Only things developers need to do is
|
|
write the interface file and include appropriate headers in the application
|
|
code (discussed in following section)
|
|
|
|
|
|
\subsection{Application}
|
|
|
|
In this section, we discuss how would we write the application code,
|
|
and how to compile it. For sake of simplicity, we will write both server and
|
|
client code in same application. Following is the code for the \texttt{main}
|
|
function, which runs the client code or server code based on the
|
|
command line argument.
|
|
|
|
|
|
\begin{verbatim}
|
|
int main(int argc, char *argv[]) {
|
|
errval_t err;
|
|
if ((argc >= 2) && (strcmp(argv[1], "client") == 0)) {
|
|
start_client();
|
|
} else if ((argc >= 2) && (strcmp(argv[1], "server") == 0)) {
|
|
start_server();
|
|
} else {
|
|
return EXIT_FAILURE;
|
|
}
|
|
/* The dispatch loop */
|
|
struct waitset *ws = get_default_waitset();
|
|
while (1) {
|
|
err = event_dispatch(ws); /* get and handle next event */
|
|
if (err_is_fail(err)) {
|
|
DEBUG_ERR(err, "in event_dispatch");
|
|
break;
|
|
}
|
|
}
|
|
return EXIT_FAILURE;
|
|
}
|
|
\end{verbatim}
|
|
|
|
The important part of the above is in the dispatch loop where application
|
|
will wait for events on the default wait-set and handle those events
|
|
in infinite while loop.
|
|
|
|
\subsection{Server side}
|
|
|
|
In this section, we will develop the server code in steps of
|
|
"exporting service", "registering the service" and "handling actual requests".
|
|
|
|
|
|
\subsubsection{Exporting service}
|
|
As a first step, server needs to export the service it wants to provide
|
|
by calling export function on the service interface. In this case, server
|
|
will call \texttt{hello\_export} as we are providing \textit{Hello World}
|
|
service.
|
|
|
|
\begin{verbatim}
|
|
static void start_server(void)
|
|
{
|
|
errval_t err;
|
|
err = hello_export(NULL /* state for callbacks */,
|
|
export_cb, /* Callback for export */
|
|
connect_cb, /* Callback for client connects */
|
|
get_default_waitset(), /* waitset where events will be sent */
|
|
IDC_EXPORT_FLAGS_DEFAULT);
|
|
if (err_is_fail(err)) {
|
|
USER_PANIC_ERR(err, "export failed");
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
|
|
This call also registers two callback handles. Once the service
|
|
is successfully exported, then the export callback provided. When any client
|
|
connects with the service then the connect callback will be called.
|
|
We will see the use of these callbacks in next few steps.
|
|
|
|
|
|
\subsubsection{Registering service}
|
|
Server also need to register the service so that other applications can find
|
|
it. Following code registers the \texttt{"hello\_service"} on the callback
|
|
from successful completion of export:
|
|
|
|
|
|
\begin{verbatim}
|
|
const static char *service_name = "hello_service";
|
|
static void export_cb(void *st, errval_t err, iref_t iref)
|
|
{
|
|
if (err_is_fail(err)) {
|
|
USER_PANIC_ERR(err, "export failed");
|
|
}
|
|
err = nameservice_register(service_name, iref);
|
|
if (err_is_fail(err)) {
|
|
USER_PANIC_ERR(err, "nameservice_register failed");
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
|
|
The \texttt{nameservice\_register} is a blocking call which will connect
|
|
to the global nameserver and publish the service name.
|
|
|
|
\subsubsection{Connect and handling messages}
|
|
This section describes how exactly the connection is established and
|
|
requests are handled.
|
|
|
|
\begin{verbatim}
|
|
static void rx_hello_msg(struct hello_binding *b, char *str)
|
|
{
|
|
printf("server: received hello_msg:\n\t%s\n", str);
|
|
free(str);
|
|
}
|
|
|
|
static struct hello_rx_vtbl rx_vtbl = {
|
|
.hello_msg = rx_hello_msg,
|
|
};
|
|
|
|
static errval_t connect_cb(void *st, struct hello_binding *b)
|
|
{
|
|
b->rx_vtbl = rx_vtbl;
|
|
return SYS_ERR_OK;
|
|
}
|
|
\end{verbatim}
|
|
|
|
The above code defines a function \texttt{rx\_hello\_msg} which will be
|
|
responsible to actually handle the requests. In our case, we are just
|
|
printing out the string we received as part of the message.
|
|
|
|
We need to create a list of function pointers where one function pointer
|
|
is assigned for every possible incoming message. So we are creating
|
|
an instance \texttt{rx\_vtbl} of type \texttt{hello\_rx\_vtbl}.
|
|
|
|
On arrival of new connection, we provide this list of function pointers
|
|
to register which functions to call for handling particular type of message.
|
|
|
|
|
|
\subsection{Client side}
|
|
Client needs to find the service and connect to it. Once the connection
|
|
is successfully established then it can send the actual requests. In this
|
|
section, we show how it can be done.
|
|
|
|
|
|
\subsubsection{Find and Bind}
|
|
Following code finds the desired service with given name and binds with
|
|
the service.
|
|
|
|
\begin{verbatim}
|
|
static void start_client(void)
|
|
{
|
|
errval_t err;
|
|
iref_t iref;
|
|
err = nameservice_blocking_lookup(service_name,
|
|
&iref);
|
|
if (err_is_fail(err)) {
|
|
USER_PANIC_ERR(err,
|
|
"nameservice_blocking_lookup failed");
|
|
}
|
|
err = hello_bind(iref, bind_cb, /* Callback function */
|
|
NULL /* State for the callback */,
|
|
get_default_waitset(),
|
|
IDC_BIND_FLAGS_DEFAULT);
|
|
if (err_is_fail(err)) {
|
|
USER_PANIC_ERR(err, "bind failed");
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
|
|
As the name suggests \texttt{nameservice\_blocking\_lookup} is a blocking
|
|
call which will connect with the nameserver and query for the given
|
|
service name. The \texttt{iref} handle returned by this call can be used
|
|
for binding with the service.
|
|
|
|
The \texttt{hello\_bind} is part of the code generated from the interface
|
|
file and will try and connect with the server on appropriate channel.
|
|
|
|
Following code is callback function which will be called when client
|
|
successfully connects with the server. This code also creates a client
|
|
state which stores pointer to the communication channel binding. This code
|
|
then calls a function \texttt{run\_client} with the client state.
|
|
|
|
|
|
\begin{verbatim}
|
|
struct client_state {
|
|
struct hello_binding *binding;
|
|
int count;
|
|
};
|
|
|
|
static void bind_cb(void *st, errval_t err, struct hello_binding *b)
|
|
{
|
|
struct client_state *myst = malloc(sizeof(struct client_state));
|
|
assert(myst != NULL);
|
|
myst->binding = b;
|
|
myst->count = 0;
|
|
run_client(myst); /* calling run_client for first time */
|
|
}
|
|
\end{verbatim}
|
|
|
|
|
|
\subsubsection{Sending messages}
|
|
|
|
Most of the actual work of sending message is done from the \texttt{run\_client}
|
|
function which is described bellow:
|
|
|
|
\begin{verbatim}
|
|
static void run_client(void *arg)
|
|
{
|
|
errval_t err;
|
|
struct client_state *myst = arg;
|
|
struct hello_binding *b = myst->binding;
|
|
|
|
/* Creating a continuation which will call run_client */
|
|
struct event_closure txcont = MKCONT(run_client, myst);
|
|
|
|
err = b->tx_vtbl.hello_msg(b, txcont, "Hello World");
|
|
if (err_is_fail(err)) {
|
|
DEBUG_ERR(err, "error sending message %d\n", myst->count);
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
|
|
This function first creates a continuation which calls itself
|
|
and will be used as a callback function. The next step is to actually
|
|
send the message by calling an appropriate function on the send side of
|
|
interface binding (\texttt{tx\_vtbl.hello\_msg}) with actual message
|
|
and above created continuation. This call will register a message to be sent
|
|
and a callback that will be triggered when message is successfully.
|
|
|
|
As you can notice, callback function is calling \texttt{run\_client} again,
|
|
leading an infinite loop of sending messages.
|
|
|
|
\subsubsection{Include files}
|
|
|
|
Following is a typical set of include files that you will need to add
|
|
in your code.
|
|
|
|
\begin{verbatim}
|
|
#include <stdio.h>
|
|
#include <barrelfish/barrelfish.h>
|
|
#include <barrelfish/nameservice_client.h>
|
|
#include <if/hello_defs.h>
|
|
\end{verbatim}
|
|
|
|
The \texttt{barrelfish.h} file contains declaration of
|
|
functionalities related to Barrelfish OS (eg: system calls, capability
|
|
system related functionalities, etc.)
|
|
\texttt{nameservice\_client.h} file provides declarations for functions related
|
|
to registering and looking up services. The \texttt{hello\_defs.h} file
|
|
includes the declarations for automatically generated code to support
|
|
\texttt{if/hello.if} interface.
|
|
|
|
|
|
\subsection{Building and running the application}
|
|
|
|
In this section, we talk about how build your application. For this, you
|
|
will need to create a \texttt{Hakefile} in the code directory which should
|
|
look something like bellow:
|
|
|
|
\begin{verbatim}
|
|
[ build application { target = "hello-cs",
|
|
cFiles = [ "hello.c" ],
|
|
flounderBindings = [ "hello" ]
|
|
}
|
|
]
|
|
\end{verbatim}
|
|
|
|
This \texttt{Hakefile} specifies that you want to build an application
|
|
with name \texttt{hello\-cs} from the \texttt{hello.c} file, and also
|
|
it marks the dependency on the communication interface \texttt{hello}.
|
|
|
|
The next step is to modify the \texttt{barrelfish/hake/symbolic\_targets.mk}
|
|
file to include your newly created application into the build process.
|
|
Here you can choose an architecture for which your application should be
|
|
compiled, or you can also add your application in \texttt{MODULES\_COMMON}
|
|
list so that your application will be compiled for all architectures.
|
|
|
|
\begin{verbatim}
|
|
MODULES_COMMON= \
|
|
sbin/init_null \
|
|
...
|
|
...
|
|
sbin/xcorecapbench \
|
|
sbin/hello-cs \
|
|
|
|
\end{verbatim}
|
|
|
|
After this, rebuild the Barrelfish to include newly added application. On
|
|
successful compilation the binary will be created in sbin directory of
|
|
desired architecture.
|
|
|
|
|
|
% ########### Editing menu.lst
|
|
Next step is to edit one of the \texttt{barrelfish/hake/menu.lst.*} file based
|
|
on which architecture you are targeting. Bellow is the minimal
|
|
\texttt{menu.lst.x86\_64} file to be able to run this newly created application.
|
|
|
|
\begin{verbatim}
|
|
title Barrelfish
|
|
root (nd)
|
|
kernel /x86_64/sbin/elver loglevel=3
|
|
module /x86_64/sbin/cpu loglevel=3
|
|
module /x86_64/sbin/init
|
|
|
|
# Domains spawned by init
|
|
module /x86_64/sbin/mem_serv
|
|
module /x86_64/sbin/monitor
|
|
|
|
# Special boot time domains spawned by monitor
|
|
module /x86_64/sbin/ramfsd boot
|
|
module /x86_64/sbin/skb boot
|
|
modulenounzip /skb_ramfs.cpio.gz nospawn
|
|
module /x86_64/sbin/kaluga boot
|
|
module /x86_64/sbin/acpi boot
|
|
module /x86_64/sbin/spawnd boot
|
|
#bootapic-x86_64=1-15
|
|
module /x86_64/sbin/startd boot
|
|
module /x86_64/sbin/routing_setup boot
|
|
|
|
# Drivers
|
|
module /x86_64/sbin/pci auto
|
|
module /x86_64/sbin/ahcid auto
|
|
|
|
# General user domains
|
|
module /x86_64/sbin/serial
|
|
|
|
# Your hello world application (both server and client)
|
|
module /x86_64/sbin/hello-cs core=0 server
|
|
module /x86_64/sbin/hello-cs core=1 client
|
|
\end{verbatim}
|
|
|
|
Now, you can build your application and verify that binary is created as
|
|
follows:
|
|
|
|
\begin{verbatim}
|
|
$ make
|
|
$ ls x86_64/sbin/hello-cs
|
|
\end{verbatim}
|
|
|
|
If everything goes fine, you can run the application in \texttt{qemu} simulator
|
|
with following command:
|
|
\begin{verbatim}
|
|
$ make sim
|
|
\end{verbatim}
|
|
|
|
At this moment, you should be able see Barrelfish booting and starting your
|
|
applications which are then able to communicate by sending "Hello World"
|
|
message in infinite loop. Following is a sample output that you may expect
|
|
while running this setup.
|
|
|
|
\begin{verbatim}
|
|
...
|
|
...
|
|
...
|
|
startd.0: starting app /x86_64/sbin/hello-cs on core 0
|
|
spawnd.0: spawning /x86_64/sbin/hello-cs on core 0
|
|
startd.0: starting app /x86_64/sbin/hello-cs on core 1
|
|
skb.0: waiting for: spawn.1
|
|
skb.0: waiting for: pci
|
|
spawnd.0: spawning /x86_64/sbin/ahcid on core 0
|
|
kernel: 0: installing handler for IRQ 1
|
|
kernel: 0: installing handler for IRQ 2
|
|
pci_client.c: got vector 2
|
|
ahcid: registered device 8086:2922
|
|
spawnd.1: spawning /x86_64/sbin/hello-cs on core 1
|
|
No bootscript
|
|
server: received hello_msg:
|
|
Hello World
|
|
server: received hello_msg:
|
|
Hello World
|
|
server: received hello_msg:
|
|
Hello World
|
|
server: received hello_msg:
|
|
Hello World
|
|
server: received hello_msg:
|
|
Hello World
|
|
server: received hello_msg:
|
|
Hello World
|
|
...
|
|
...
|
|
...
|
|
\end{verbatim}
|
|
|
|
|