2 \section{Writing A Sample Barrelfish Application}
4 Here we show how to write a simple program on Barrelfish. \textbf{XXX:} no
5 error handling yet. Refer to \texttt{usr/tests/idctest} for code with proper
8 \subsection{A Simple Hello World Program}
10 The source for user domains is stored under \begin{verbatim}/usr\end{verbatim}.
12 A simple hello world domain consists of a source file, and a Hakefile. e.g.,
19 For a simple hello world app, the source is trivial: a simple printf in main
20 will suffice (note that for output printf either uses a debug syscall to print
21 to the serial output, or a proper serial server if one is available).
26 printf("Hello World\n");
31 To build the domain, call hake.sh in the build directory. This will create a
32 Config.hs, Makefile, and symbolic\_targets.mk. You can add myapp in the
33 symbolic\_tagets, then run make to build everything. You can also explicitly ask
34 make to build the domain. e.g.:
36 make x86_64/sbin/myapp
39 After all is built, the menu.lst needs to be modified to add the domain and have
40 it started. Run everything in a simulator using:
46 \section{A Simple Hello World Program Using IDC}
48 In this section, we talk how we can write an application that uses Barrelfish
49 IDC mechanism to communicate with other applications. For purpose of this
50 section, we will develop simple \textbf{Hello World} client and server
51 application. The client running on core-0 will send an IDC message to a
52 server running on core-1. This message will contain a string with contents
53 \texttt{"Hello World"}.
55 Adding IDC (inter-dispatcher communication) is a bit more complicated. It
56 requires defining an interface in Flounder IDL, updating the interfaces
57 Hakefile, updating the domain's (myapp) Hakefile, and then adding initialization
58 code as well as appropriate callbacks to the source file.
62 \includegraphics[width=0.8\columnwidth]{helloWorld2.pdf}
64 \caption{Hello world application overview}
65 \label{fig:helloWorld}
68 \subsection{Interface}
70 In this subsection, we will create a new interface
71 for our use. We do this by creating a new interface file called
72 \texttt{hello.if} in the \texttt{if\\} directory. Following is simple interface
73 that will suffice for our requirements of sending single string as a message.
76 \verbatiminput{if/hello.if}
78 interface hello "Hello World interface" {
79 message hello_msg(string s);
83 You can find out more about how to write such interfaces by referring
84 \href{http://www.barrelfish.org/TN-011-IDC.pdf}{IDC-Technote}.
86 You also need to modify the \texttt{if/Hakefile} to add this newly created
87 interface into the compilation process.
90 [ flounderGenDefs (options arch) f
95 arch <- allArchitectures
99 Adding the name of your interface here will make sure that the communication
100 stubs will be automatically generated for by the compilation process. These
101 can be found in following location in your build directory:
104 BUILD/ARCH/include/if/hello_defs.h
105 BUILD/ARCH/include/if/hello_lmp_defs.h
106 BUILD/ARCH/include/if/hello_ump_defs.h
107 BUILD/ARCH/include/if/hello_multihop_defs.h
110 There will be one for each type of communication mechanism supported by
111 the Barrelfish. You don't need to worry about these files as build mechanism
112 will take care of these files. Only things developers need to do is
113 write the interface file and include appropriate headers in the application
114 code (discussed in following section)
117 \subsection{Application}
119 In this section, we discuss how would we write the application code,
120 and how to compile it. For sake of simplicity, we will write both server and
121 client code in same application. Following is the code for the \texttt{main}
122 function, which runs the client code or server code based on the
123 command line argument.
127 int main(int argc, char *argv[]) {
129 if ((argc >= 2) && (strcmp(argv[1], "client") == 0)) {
131 } else if ((argc >= 2) && (strcmp(argv[1], "server") == 0)) {
136 /* The dispatch loop */
137 struct waitset *ws = get_default_waitset();
139 err = event_dispatch(ws); /* get and handle next event */
140 if (err_is_fail(err)) {
141 DEBUG_ERR(err, "in event_dispatch");
149 The important part of the above is in the dispatch loop where application
150 will wait for events on the default wait-set and handle those events
151 in infinite while loop.
153 \subsection{Server side}
155 In this section, we will develop the server code in steps of
156 "exporting service", "registering the service" and "handling actual requests".
159 \subsubsection{Exporting service}
160 As a first step, server needs to export the service it wants to provide
161 by calling export function on the service interface. In this case, server
162 will call \texttt{hello\_export} as we are providing \textit{Hello World}
166 static void start_server(void)
169 err = hello_export(NULL /* state for callbacks */,
170 export_cb, /* Callback for export */
171 connect_cb, /* Callback for client connects */
172 get_default_waitset(), /* waitset where events will be sent */
173 IDC_EXPORT_FLAGS_DEFAULT);
174 if (err_is_fail(err)) {
175 USER_PANIC_ERR(err, "export failed");
180 This call also registers two callback handles. Once the service
181 is successfully exported, then the export callback provided. When any client
182 connects with the service then the connect callback will be called.
183 We will see the use of these callbacks in next few steps.
186 \subsubsection{Registering service}
187 Server also need to register the service so that other applications can find
188 it. Following code registers the \texttt{"hello\_service"} on the callback
189 from successful completion of export:
193 const static char *service_name = "hello_service";
194 static void export_cb(void *st, errval_t err, iref_t iref)
196 if (err_is_fail(err)) {
197 USER_PANIC_ERR(err, "export failed");
199 err = nameservice_register(service_name, iref);
200 if (err_is_fail(err)) {
201 USER_PANIC_ERR(err, "nameservice_register failed");
206 The \texttt{nameservice\_register} is a blocking call which will connect
207 to the global nameserver and publish the service name.
209 \subsubsection{Connect and handling messages}
210 This section describes how exactly the connection is established and
211 requests are handled.
214 static void rx_hello_msg(struct hello_binding *b, char *str)
216 printf("server: received hello_msg:\n\t%s\n", str);
220 static struct hello_rx_vtbl rx_vtbl = {
221 .hello_msg = rx_hello_msg,
224 static errval_t connect_cb(void *st, struct hello_binding *b)
226 b->rx_vtbl = rx_vtbl;
231 The above code defines a function \texttt{rx\_hello\_msg} which will be
232 responsible to actually handle the requests. In our case, we are just
233 printing out the string we received as part of the message.
235 We need to create a list of function pointers where one function pointer
236 is assigned for every possible incoming message. So we are creating
237 an instance \texttt{rx\_vtbl} of type \texttt{hello\_rx\_vtbl}.
239 On arrival of new connection, we provide this list of function pointers
240 to register which functions to call for handling particular type of message.
243 \subsection{Client side}
244 Client needs to find the service and connect to it. Once the connection
245 is successfully established then it can send the actual requests. In this
246 section, we show how it can be done.
249 \subsubsection{Find and Bind}
250 Following code finds the desired service with given name and binds with
254 static void start_client(void)
258 err = nameservice_blocking_lookup(service_name,
260 if (err_is_fail(err)) {
262 "nameservice_blocking_lookup failed");
264 err = hello_bind(iref, bind_cb, /* Callback function */
265 NULL /* State for the callback */,
266 get_default_waitset(),
267 IDC_BIND_FLAGS_DEFAULT);
268 if (err_is_fail(err)) {
269 USER_PANIC_ERR(err, "bind failed");
274 As the name suggests \texttt{nameservice\_blocking\_lookup} is a blocking
275 call which will connect with the nameserver and query for the given
276 service name. The \texttt{iref} handle returned by this call can be used
277 for binding with the service.
279 The \texttt{hello\_bind} is part of the code generated from the interface
280 file and will try and connect with the server on appropriate channel.
282 Following code is callback function which will be called when client
283 successfully connects with the server. This code also creates a client
284 state which stores pointer to the communication channel binding. This code
285 then calls a function \texttt{run\_client} with the client state.
289 struct client_state {
290 struct hello_binding *binding;
294 static void bind_cb(void *st, errval_t err, struct hello_binding *b)
296 struct client_state *myst = malloc(sizeof(struct client_state));
297 assert(myst != NULL);
300 run_client(myst); /* calling run_client for first time */
305 \subsubsection{Sending messages}
307 Most of the actual work of sending message is done from the \texttt{run\_client}
308 function which is described bellow:
311 static void run_client(void *arg)
314 struct client_state *myst = arg;
315 struct hello_binding *b = myst->binding;
317 /* Creating a continuation which will call run_client */
318 struct event_closure txcont = MKCONT(run_client, myst);
320 err = b->tx_vtbl.hello_msg(b, txcont, "Hello World");
321 if (err_is_fail(err)) {
322 DEBUG_ERR(err, "error sending message %d\n", myst->count);
327 This function first creates a continuation which calls itself
328 and will be used as a callback function. The next step is to actually
329 send the message by calling an appropriate function on the send side of
330 interface binding (\texttt{tx\_vtbl.hello\_msg}) with actual message
331 and above created continuation. This call will register a message to be sent
332 and a callback that will be triggered when message is successfully.
334 As you can notice, callback function is calling \texttt{run\_client} again,
335 leading an infinite loop of sending messages.
337 \subsubsection{Include files}
339 Following is a typical set of include files that you will need to add
344 #include <barrelfish/barrelfish.h>
345 #include <barrelfish/nameservice_client.h>
346 #include <if/hello_defs.h>
349 The \texttt{barrelfish.h} file contains declaration of
350 functionalities related to Barrelfish OS (eg: system calls, capability
351 system related functionalities, etc.)
352 \texttt{nameservice\_client.h} file provides declarations for functions related
353 to registering and looking up services. The \texttt{hello\_defs.h} file
354 includes the declarations for automatically generated code to support
355 \texttt{if/hello.if} interface.
358 \subsection{Building and running the application}
360 In this section, we talk about how build your application. For this, you
361 will need to create a \texttt{Hakefile} in the code directory which should
362 look something like bellow:
365 [ build application { target = "hello-cs",
366 cFiles = [ "hello.c" ],
367 flounderBindings = [ "hello" ]
372 This \texttt{Hakefile} specifies that you want to build an application
373 with name \texttt{hello\-cs} from the \texttt{hello.c} file, and also
374 it marks the dependency on the communication interface \texttt{hello}.
376 The next step is to modify the \texttt{barrelfish/hake/symbolic\_targets.mk}
377 file to include your newly created application into the build process.
378 Here you can choose an architecture for which your application should be
379 compiled, or you can also add your application in \texttt{MODULES\_COMMON}
380 list so that your application will be compiled for all architectures.
392 After this, rebuild the Barrelfish to include newly added application. On
393 successful compilation the binary will be created in sbin directory of
394 desired architecture.
397 % ########### Editing menu.lst
398 Next step is to edit one of the \texttt{barrelfish/hake/menu.lst.*} file based
399 on which architecture you are targeting. Bellow is the minimal
400 \texttt{menu.lst.x86\_64} file to be able to run this newly created application.
405 kernel /x86_64/sbin/elver loglevel=4
406 module /x86_64/sbin/cpu loglevel=4
407 module /x86_64/sbin/init
409 # Domains spawned by init
410 module /x86_64/sbin/mem_serv
411 module /x86_64/sbin/monitor
413 # Special boot time domains spawned by monitor
414 module /x86_64/sbin/ramfsd boot
415 module /x86_64/sbin/skb boot
416 modulenounzip /skb_ramfs.cpio.gz nospawn
417 module /x86_64/sbin/kaluga boot
418 module /x86_64/sbin/acpi boot
419 module /x86_64/sbin/spawnd boot
420 #bootapic-x86_64=1-15
421 module /x86_64/sbin/startd boot
422 module /x86_64/sbin/routing_setup boot
425 module /x86_64/sbin/pci auto
426 module /x86_64/sbin/ahcid auto
428 # General user domains
429 module /x86_64/sbin/serial
431 # Your hello world application (both server and client)
432 module /x86_64/sbin/hello-cs core=0 server
433 module /x86_64/sbin/hello-cs core=1 client
436 Now, you can build your application and verify that binary is created as
441 $ ls x86_64/sbin/hello-cs
444 If everything goes fine, you can run the application in \texttt{qemu} simulator
445 with following command:
450 At this moment, you should be able see Barrelfish booting and starting your
451 applications which are then able to communicate by sending "Hello World"
452 message in infinite loop. Following is a sample output that you may expect
453 while running this setup.
459 startd.0: starting app /x86_64/sbin/hello-cs on core 0
460 spawnd.0: spawning /x86_64/sbin/hello-cs on core 0
461 startd.0: starting app /x86_64/sbin/hello-cs on core 1
462 skb.0: waiting for: spawn.1
463 skb.0: waiting for: pci
464 spawnd.0: spawning /x86_64/sbin/ahcid on core 0
465 kernel: 0: installing handler for IRQ 1
466 kernel: 0: installing handler for IRQ 2
467 pci_client.c: got vector 2
468 ahcid: registered device 8086:2922
469 spawnd.1: spawning /x86_64/sbin/hello-cs on core 1
471 server: received hello_msg:
473 server: received hello_msg:
475 server: received hello_msg:
477 server: received hello_msg:
479 server: received hello_msg:
481 server: received hello_msg: