Update of the hello tutorial
This commit is contained in:
parent
357b84835a
commit
92a10541aa
|
@ -1,2 +1,2 @@
|
||||||
This repository contains the source code of the 'Hello' client-server tutorial
|
This repository contains the source code of a simple client-server scenario
|
||||||
written by Björn Döbel. Please find the document at 'doc/hello_tutorial.txt'.
|
using Genode's RPC mechanism.
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
<config>
|
|
||||||
<parent-provides>
|
|
||||||
<service name="LOG"/>
|
|
||||||
<service name="RM"/>
|
|
||||||
|
|
||||||
<!-- some timer implementations need kernel info pages -->
|
|
||||||
<service name="ROM"/>
|
|
||||||
|
|
||||||
<!-- hardware-based timers need I/O resources -->
|
|
||||||
<service name="IO_MEM"/>
|
|
||||||
<service name="IO_PORT"/>
|
|
||||||
<service name="IRQ"/>
|
|
||||||
</parent-provides>
|
|
||||||
<default-route>
|
|
||||||
<any-service> <parent/> <any-child/> </any-service>
|
|
||||||
</default-route>
|
|
||||||
|
|
||||||
<start name="hello_server">
|
|
||||||
<resource name="RAM" quantum="1M"/>
|
|
||||||
<provides><service name="Hello"/></provides>
|
|
||||||
</start>
|
|
||||||
<start name="timer">
|
|
||||||
<resource name="RAM" quantum="1M"/>
|
|
||||||
<provides><service name="Timer"/></provides>
|
|
||||||
</start>
|
|
||||||
<start name="hello_client">
|
|
||||||
<resource name="RAM" quantum="1M"/>
|
|
||||||
</start>
|
|
||||||
</config>
|
|
|
@ -1,32 +1,25 @@
|
||||||
|
|
||||||
Creating your first Genode application
|
A simple client-server scenario
|
||||||
|
|
||||||
Björn Döbel and Norman Feske
|
Björn Döbel and Norman Feske
|
||||||
|
|
||||||
Abstract
|
Abstract
|
||||||
########
|
########
|
||||||
|
|
||||||
This section will give you a step-by-step introduction for writing your first
|
This tutorial will give you a step-by-step introduction for creating your first
|
||||||
little client-server application using the Genode OS Framework. We will create
|
little client-server application scenario using the Genode OS Framework. We will create
|
||||||
a server that provides two functions to its clients and a client that uses
|
a server that provides two functions to its clients and a client that uses
|
||||||
these functions. The code samples in this section are not necessarily complete.
|
these functions. The code samples in this section are not necessarily complete.
|
||||||
You can download the complete tutorial source code from the link at the bottom
|
You can can find the complete source code at the _repos/hello_tutorial_
|
||||||
of this page.
|
directory within Genode's source tree.
|
||||||
|
|
||||||
|
|
||||||
Prerequisites
|
Prerequisites
|
||||||
#############
|
#############
|
||||||
|
|
||||||
We assume that you know how to write code and have read:
|
We assume that you have acquainted yourself with the basic concepts of
|
||||||
|
Genode and have read the "Getting started" section of the Genode Foundations
|
||||||
Norman Feske and Christian Helmuth:
|
book. Our can download the book from [http://genode.org].
|
||||||
"Design of the Genode OS Architecture",
|
|
||||||
_TU Dresden technical report TUD-FI06-07, Dresden, Germany, December 2006_.
|
|
||||||
|
|
||||||
[http://genode-labs.com/publications/bastei-design-2006.pdf]
|
|
||||||
|
|
||||||
so that you have a basic understanding of what Genode is and how things work.
|
|
||||||
Of course, you will also need to check out Genode before going any further.
|
|
||||||
|
|
||||||
|
|
||||||
Setting up the build environment
|
Setting up the build environment
|
||||||
|
@ -46,16 +39,16 @@ following subdirectory structure:
|
||||||
! hello_tutorial/src/hello/client
|
! hello_tutorial/src/hello/client
|
||||||
|
|
||||||
In the remaining document when referring to non-absolute directories, these are
|
In the remaining document when referring to non-absolute directories, these are
|
||||||
local to 'hello_tutorial'.
|
local to _hello_tutorial_.
|
||||||
Now we tell the Genode build system, that there is a new repository. Therefore
|
Now we tell the Genode build system that there is a new repository. Therefore
|
||||||
we add the path to our new repository to 'build/etc/build.conf':
|
we add the path to our new repository to _build/etc/build.conf_:
|
||||||
|
|
||||||
! REPOSITORIES += /path/to/your/hello_tutorial
|
! REPOSITORIES += /path/to/your/hello_tutorial
|
||||||
|
|
||||||
Later we will place build description files into the tutorial subdirectories so
|
Later we will place build description files into the tutorial subdirectories
|
||||||
that the build system can figure out what is needed to build your applications.
|
so that the build system can figure out what is needed to build your custom
|
||||||
You can then build these apps from the 'build' directory using one of the
|
components. You can then build these components from the _build_ directory
|
||||||
following commands:
|
using one of the following commands:
|
||||||
|
|
||||||
! make hello
|
! make hello
|
||||||
! make hello/server
|
! make hello/server
|
||||||
|
@ -67,8 +60,8 @@ commands build only the specific target respectively.
|
||||||
Defining an interface
|
Defining an interface
|
||||||
#####################
|
#####################
|
||||||
|
|
||||||
In our example we are going to implement a server providing two functions:
|
In our example, we are going to implement a server providing two functions:
|
||||||
:'void say_hello()': makes the server print "Hello world."
|
:'void say_hello()': makes the server print a message, and
|
||||||
:'int add(int a, int b)': adds two integers and returns the result.
|
:'int add(int a, int b)': adds two integers and returns the result.
|
||||||
|
|
||||||
The interface of a Genode service is called a _session_. We will define it as a
|
The interface of a Genode service is called a _session_. We will define it as a
|
||||||
|
@ -77,36 +70,35 @@ C++ class in 'include/hello_session/hello_session.h'
|
||||||
!#include <session/session.h>
|
!#include <session/session.h>
|
||||||
!#include <base/rpc.h>
|
!#include <base/rpc.h>
|
||||||
!
|
!
|
||||||
!namespace Hello {
|
!namespace Hello { struct Session; }
|
||||||
!
|
!
|
||||||
! struct Session : public Genode::Session
|
!struct Hello::Session : Genode::Session
|
||||||
! {
|
!{
|
||||||
! static const char *service_name() { return "Hello"; }
|
! static const char *service_name() { return "Hello"; }
|
||||||
!
|
!
|
||||||
! virtual void say_hello() = 0;
|
! virtual void say_hello() = 0;
|
||||||
! virtual int add(int a, int b) = 0;
|
! virtual int add(int a, int b) = 0;
|
||||||
!
|
!
|
||||||
! GENODE_RPC(Rpc_say_hello, void, say_hello);
|
! GENODE_RPC(Rpc_say_hello, void, say_hello);
|
||||||
! GENODE_RPC(Rpc_add, int, add, int, int);
|
! GENODE_RPC(Rpc_add, int, add, int, int);
|
||||||
! GENODE_RPC_INTERFACE(Rpc_say_hello, Rpc_add);
|
! GENODE_RPC_INTERFACE(Rpc_say_hello, Rpc_add);
|
||||||
! };
|
!};
|
||||||
!}
|
|
||||||
|
|
||||||
As a good practice, we place the Hello service into a dedicated namespace. The
|
As a good practice, we place the Hello service into a dedicated namespace. The
|
||||||
_Hello::Session_ class defines the public interface for our service as well as
|
_Hello::Session_ class defines the public interface for our service as well as
|
||||||
the meta information that Genode needs to perform remote procedure calls (RPC)
|
the meta information that Genode needs to perform remote procedure calls (RPC)
|
||||||
accross process boundaries.
|
across component boundaries.
|
||||||
Furthermore, we use the interface to specify the name of the
|
Furthermore, we use the interface to specify the name of the service by
|
||||||
service by using the 'service_name' function. This function will later
|
providing the 'service_name' method. This method will later be used by both
|
||||||
be used by both the server for announcing the service at its parent and
|
the server for announcing the service at its parent and the client for
|
||||||
the client for requesting the creation of a "Hello" session.
|
requesting the creation of a "Hello" session.
|
||||||
|
|
||||||
The 'GENODE_RPC' macro is used to declare an RPC function. Its first argument
|
The 'GENODE_RPC' macro is used to declare an RPC function. Its first argument
|
||||||
is a type name that is used to refer to the RPC function. The type name can
|
is a type name that is used to refer to the RPC function. The type name can
|
||||||
be choosen freely. However, it is a good practice to prefix the type name
|
be chosen freely. However, it is a good practice to prefix the type name
|
||||||
with 'Rpc_'. The remaining arguments are the return type of the RPC function,
|
with 'Rpc_'. The remaining arguments are the return type of the RPC function,
|
||||||
the server-side name of the RPC implementation, and the function arguments.
|
the server-side name of the RPC implementation, and the function arguments.
|
||||||
The 'GENODE_RPC_INTERFACE' macros declares the list of RPC functions that the
|
The 'GENODE_RPC_INTERFACE' macro declares the list of RPC functions that the
|
||||||
RPC interface is comprised of. Under the hood, the 'GENODE_RPC*' macros enrich
|
RPC interface is comprised of. Under the hood, the 'GENODE_RPC*' macros enrich
|
||||||
the compound class with the type information used to automatically generate the
|
the compound class with the type information used to automatically generate the
|
||||||
RPC communication code at compile time. They do not add any members to the
|
RPC communication code at compile time. They do not add any members to the
|
||||||
|
@ -129,21 +121,20 @@ instantiating this template class with the session interface as argument, the
|
||||||
'Session_component' class gets equipped with the communication code that
|
'Session_component' class gets equipped with the communication code that
|
||||||
will make the server's functions accessible via RPC.
|
will make the server's functions accessible via RPC.
|
||||||
|
|
||||||
!#include <base/printf.h>
|
!#include <base/log.h>
|
||||||
!#include <hello_session/hello_session.h>
|
!#include <hello_session/hello_session.h>
|
||||||
!#include <base/rpc_server.h>
|
!#include <base/rpc_server.h>
|
||||||
!
|
!
|
||||||
!namespace Hello {
|
!namespace Hello { struct Session_component; }
|
||||||
!
|
!
|
||||||
! struct Session_component : Genode::Rpc_object<Session>
|
!struct Hello::Session_component : Genode::Rpc_object<Session>
|
||||||
! {
|
!{
|
||||||
! void say_hello() {
|
! void say_hello() {
|
||||||
! PDBG("I am here... Hello."); }
|
! Genode::log("I am here... Hello."); }
|
||||||
!
|
!
|
||||||
! int add(int a, int b) {
|
! int add(int a, int b) {
|
||||||
! return a + b; }
|
! return a + b; }
|
||||||
! };
|
!};
|
||||||
!}
|
|
||||||
|
|
||||||
|
|
||||||
Getting ready to start
|
Getting ready to start
|
||||||
|
@ -157,82 +148,80 @@ application. Starting a service with Genode works as follows:
|
||||||
client to communicate with the server.
|
client to communicate with the server.
|
||||||
|
|
||||||
The class 'Hello::Root_component' is derived from Genode's 'Root_component'
|
The class 'Hello::Root_component' is derived from Genode's 'Root_component'
|
||||||
class template. This class defines a '_create_session' method which is called
|
class template. This class defines a '_create_session' method, which is called
|
||||||
each time a client wants to establish a connection to the server. This function
|
each time a client wants to establish a connection to the server. This function
|
||||||
is responsible for parsing the parameter string the client hands over to the
|
is responsible for parsing the parameter string the client hands over to the
|
||||||
server and creating a 'Hello::Session_component' object from these parameters.
|
server and for creating a 'Hello::Session_component' object from these
|
||||||
|
parameters.
|
||||||
|
|
||||||
!#include <base/printf.h>
|
!#include <base/log.h>
|
||||||
!#include <root/component.h>
|
!#include <root/component.h>
|
||||||
!
|
!
|
||||||
!namespace Hello {
|
!namespace Hello { class Root_component; }
|
||||||
!
|
!
|
||||||
! class Root_component : public Genode::Root_component<Session_component>
|
!class Hello::Root_component
|
||||||
! {
|
!:
|
||||||
! protected:
|
! public Genode::Root_component<Session_component>
|
||||||
!
|
|
||||||
! Session_component *_create_session(const char *args)
|
|
||||||
! {
|
|
||||||
! PDBG("creating hello session.");
|
|
||||||
! return new (md_alloc()) Session_component();
|
|
||||||
! }
|
|
||||||
!
|
|
||||||
! public:
|
|
||||||
!
|
|
||||||
! Root_component(Genode::Rpc_entrypoint *ep,
|
|
||||||
! Genode::Allocator *allocator)
|
|
||||||
! : Genode::Root_component<Session_component>(ep, allocator)
|
|
||||||
! {
|
|
||||||
! PDBG("Creating root component.");
|
|
||||||
! }
|
|
||||||
! };
|
|
||||||
!}
|
|
||||||
|
|
||||||
Now we only need a main method that announces the service to our parent:
|
|
||||||
|
|
||||||
!#include <base/sleep.h>
|
|
||||||
!#include <cap_session/connection.h>
|
|
||||||
!
|
|
||||||
!using namespace Genode;
|
|
||||||
!
|
|
||||||
!int main(void)
|
|
||||||
!{
|
!{
|
||||||
! /*
|
! protected:
|
||||||
! * Get a session for the parent's capability service, so that we
|
|
||||||
! * are able to create capabilities.
|
|
||||||
! */
|
|
||||||
! Cap_connection cap;
|
|
||||||
!
|
!
|
||||||
! /*
|
! Session_component *_create_session(const char *args)
|
||||||
! * A sliced heap is used for allocating session objects - thereby we
|
! {
|
||||||
! * can release objects separately.
|
! Genode::log("creating hello session");
|
||||||
! */
|
! return new (md_alloc()) Session_component();
|
||||||
! static Sliced_heap sliced_heap(env()->ram_session(),
|
! }
|
||||||
! env()->rm_session());
|
|
||||||
!
|
!
|
||||||
! /*
|
! public:
|
||||||
! * Create objects for use by the framework.
|
|
||||||
! *
|
|
||||||
! * An 'Rpc_entrypoint' is created to announce our service's root
|
|
||||||
! * capability to our parent, manage incoming session creation
|
|
||||||
! * requests, and dispatch the session interface. The incoming RPC
|
|
||||||
! * requests are dispatched via a dedicated thread. The 'STACK_SIZE'
|
|
||||||
! * argument defines the size of the thread's stack. The additional
|
|
||||||
! * string argument is the name of the entry point, used for
|
|
||||||
! * debugging purposes only.
|
|
||||||
! */
|
|
||||||
! enum { STACK_SIZE = 4096 };
|
|
||||||
! static Rpc_entrypoint ep(&cap, STACK_SIZE, "hello_ep");
|
|
||||||
!
|
!
|
||||||
! static Hello::Root_component hello_root(&ep, &sliced_heap);
|
! Root_component(Genode::Entrypoint &ep,
|
||||||
! env()->parent()->announce(ep.manage(&hello_root));
|
! Genode::Allocator &alloc)
|
||||||
|
! :
|
||||||
|
! Genode::Root_component<Session_component>(ep, alloc)
|
||||||
|
! {
|
||||||
|
! Genode::log("creating root component");
|
||||||
|
! }
|
||||||
|
!};
|
||||||
|
|
||||||
|
Now we only need the actual application code that instantiates the root
|
||||||
|
component and the service to our parent. It is good practice to represent
|
||||||
|
the applications as a class called 'Main' with its constructor taking the
|
||||||
|
component's environment as argument.
|
||||||
|
|
||||||
|
!#include <base/component.h>
|
||||||
!
|
!
|
||||||
! /*
|
!namespace Hello { struct Main; }
|
||||||
! * We are done with this and only act upon client requests now.
|
|
||||||
! */
|
|
||||||
! sleep_forever();
|
|
||||||
!
|
!
|
||||||
! return 0;
|
!struct Hello::Main
|
||||||
|
!{
|
||||||
|
! Genode::Env &env;
|
||||||
|
!
|
||||||
|
! Genode::Sliced_heap sliced_heap { env.ram(), env.rm() };
|
||||||
|
!
|
||||||
|
! Hello::Root_component root { env.ep(), sliced_heap };
|
||||||
|
!
|
||||||
|
! Main(Genode::Env &env) : env(env)
|
||||||
|
! {
|
||||||
|
! env.parent().announce(env.ep().manage(root));
|
||||||
|
! }
|
||||||
|
!};
|
||||||
|
|
||||||
|
The sliced heap is used for the dynamic allocation of session objects.
|
||||||
|
It interacts with the component's RAM session to obtain the backing store
|
||||||
|
for the allocations, and the component's region map to make
|
||||||
|
backing store visible within its virtual address space.
|
||||||
|
|
||||||
|
The announcement of the service is performed by the body of the constructor by
|
||||||
|
creating a capability for the root component as return value of the 'manage'
|
||||||
|
method, and passing this capability to the parent.
|
||||||
|
|
||||||
|
The 'Component::construct' function of the hello server simply constructs a singleton
|
||||||
|
instance of 'Hello::Main' as a _static_ local variable.
|
||||||
|
|
||||||
|
!Genode::size_t Component::stack_size() { return 64*1024; }
|
||||||
|
!
|
||||||
|
!void Component::construct(Genode::Env &env)
|
||||||
|
!{
|
||||||
|
! static Hello::Main main(env);
|
||||||
!}
|
!}
|
||||||
|
|
||||||
|
|
||||||
|
@ -248,13 +237,12 @@ create a 'target.mk' file in 'src/hello/server':
|
||||||
! SRC_CC = main.cc
|
! SRC_CC = main.cc
|
||||||
! LIBS = base
|
! LIBS = base
|
||||||
|
|
||||||
To tell the init process to start the new program, we have to add a '<start>'
|
To tell the init component to start the new program, we have to add a '<start>'
|
||||||
entry to init's 'config' file, which is located at 'build/bin/config'.
|
entry to init's 'config' file, which is located at 'build/bin/config'.
|
||||||
|
|
||||||
! <config>
|
! <config>
|
||||||
! <parent-provides>
|
! <parent-provides>
|
||||||
! <service name="LOG"/>
|
! <service name="LOG"/>
|
||||||
! <service name="RM"/>
|
|
||||||
! </parent-provides>
|
! </parent-provides>
|
||||||
! <default-route>
|
! <default-route>
|
||||||
! <any-service> <parent/> <any-child/> </any-service>
|
! <any-service> <parent/> <any-child/> </any-service>
|
||||||
|
@ -265,53 +253,51 @@ entry to init's 'config' file, which is located at 'build/bin/config'.
|
||||||
! </start>
|
! </start>
|
||||||
! </config>
|
! </config>
|
||||||
|
|
||||||
For information about the configuring the init process, please refer
|
For information about the configuring concept, please refer to the
|
||||||
to [http://genode.org/documentation/developer-resources/init].
|
"System configuration" section of the Genode Foundations book.
|
||||||
|
|
||||||
Now rebuild 'core', 'init', and 'hello/server', go to 'build/bin', run './core'.
|
|
||||||
|
|
||||||
|
|
||||||
Writing client code
|
Writing client code
|
||||||
###################
|
###################
|
||||||
|
|
||||||
In the next part we are going to have a look at the client-side implementation.
|
In the next part, we are going to have a look at the client-side implementation.
|
||||||
The most basic steps here are:
|
The most basic steps here are:
|
||||||
|
|
||||||
* Get a capability for the "Hello" service from our parent
|
* Obtain a capability for the "Hello" service from our parent
|
||||||
* Invoke RPCs via the obtained capability
|
* Invoke RPCs via the obtained capability
|
||||||
|
|
||||||
|
|
||||||
A client object
|
A client object
|
||||||
===============
|
===============
|
||||||
|
|
||||||
We will encapsulate the Genode IPC interface in a 'Hello::Session_client' class.
|
We will encapsulate the Genode RPC interface in a 'Hello::Session_client' class.
|
||||||
This class derives from 'Hello:Session' and implements a client-side object.
|
This class derives from 'Hello:Session' and implements a client-side object.
|
||||||
Therefore edit 'include/hello_session/client.h':
|
Therefore edit 'include/hello_session/client.h':
|
||||||
|
|
||||||
!#include <hello_session/hello_session.h>
|
!#include <hello_session/hello_session.h>
|
||||||
!#include <base/rpc_client.h>
|
!#include <base/rpc_client.h>
|
||||||
!#include <base/printf.h>
|
!#include <base/log.h>
|
||||||
!
|
!
|
||||||
!namespace Hello {
|
!namespace Hello { struct Session_client; }
|
||||||
!
|
!
|
||||||
! struct Session_client : Genode::Rpc_client<Session>
|
!
|
||||||
|
!struct Hello::Session_client : Genode::Rpc_client<Session>
|
||||||
|
!{
|
||||||
|
! Session_client(Genode::Capability<Session> cap)
|
||||||
|
! : Genode::Rpc_client<Session>(cap) { }
|
||||||
|
!
|
||||||
|
! void say_hello()
|
||||||
! {
|
! {
|
||||||
! Session_client(Genode::Capability<Session> cap)
|
! Genode::log("issue RPC for saying hello");
|
||||||
! : Genode::Rpc_client<Session>(cap) { }
|
! call<Rpc_say_hello>();
|
||||||
|
! Genode::log("returned from 'say_hello' RPC call");
|
||||||
|
! }
|
||||||
!
|
!
|
||||||
! void say_hello()
|
! int add(int a, int b)
|
||||||
! {
|
! {
|
||||||
! PDBG("Saying Hello.");
|
! return call<Rpc_add>(a, b);
|
||||||
! call<Rpc_say_hello>();
|
! }
|
||||||
! }
|
!};
|
||||||
!
|
|
||||||
! int add(int a, int b)
|
|
||||||
! {
|
|
||||||
! return call<Rpc_add>(a, b);
|
|
||||||
! }
|
|
||||||
! };
|
|
||||||
!}
|
|
||||||
|
|
||||||
|
|
||||||
A 'Hello::Session_client' object takes a 'Capability' as constructor argument.
|
A 'Hello::Session_client' object takes a 'Capability' as constructor argument.
|
||||||
This capability is tagged with the session type and gets passed to the
|
This capability is tagged with the session type and gets passed to the
|
||||||
|
@ -320,51 +306,26 @@ code via the 'call' template function. The template argument for 'call' is the
|
||||||
RPC type as declared in the session interface.
|
RPC type as declared in the session interface.
|
||||||
|
|
||||||
|
|
||||||
Client implementation
|
A connection object
|
||||||
=====================
|
===================
|
||||||
|
|
||||||
The client-side implementation using the 'Hello::Session_client' object is pretty
|
Whereas the 'Hello::Session_client' is able to perform RPC calls to an RPC
|
||||||
straightforward. We request a capability for the Hello service from our parent.
|
object when given a capability for such an object, the question of how
|
||||||
This call blocks as long as the service has not been registered at the parent.
|
the client obtains this capability is still open.
|
||||||
Afterwards, we create a 'Hello::Session_client' object with it and invoke calls. In
|
Here, the so-called connection object enters the picture. A connection
|
||||||
addition, we use the Timer service that comes with Genode. This server
|
object has the purposes:
|
||||||
enables us to sleep for a certain amount of milliseconds.
|
|
||||||
|
|
||||||
Put this code into 'src/hello/client/main.cc':
|
* It transforms session-specific parameters into a format that can be
|
||||||
|
passed to the server along with the session request. The connection
|
||||||
|
object thereby hides the details of how the session parameters are
|
||||||
|
represented "on the wire".
|
||||||
|
|
||||||
!#include <base/env.h>
|
* It issues a session request to the parent and retrieves a session
|
||||||
!#include <base/printf.h>
|
capability as response.
|
||||||
!#include <hello_session/client.h>
|
|
||||||
!#include <timer_session/connection.h>
|
* It acts as a session-client object such that the session's RPC functions
|
||||||
!
|
can directly be called on the connection object.
|
||||||
!using namespace Genode;
|
|
||||||
!
|
|
||||||
!int main(void)
|
|
||||||
!{
|
|
||||||
! Capability<Hello::Session> h_cap =
|
|
||||||
! env()->parent()->session<Hello::Session>("foo, ram_quota=4K");
|
|
||||||
!
|
|
||||||
! Hello::Session_client h(h_cap);
|
|
||||||
!
|
|
||||||
! Timer::Connection timer;
|
|
||||||
!
|
|
||||||
! while (1) {
|
|
||||||
! h.say_hello();
|
|
||||||
! timer.msleep(1000);
|
|
||||||
!
|
|
||||||
! int foo = h.add(2,5);
|
|
||||||
! PDBG("Added 2 + 5 = %d", foo);
|
|
||||||
! timer.msleep(1000);
|
|
||||||
! }
|
|
||||||
!
|
|
||||||
! return 0;
|
|
||||||
!}
|
|
||||||
|
|
||||||
Compared to the creation of the Timer session, the creation of "Hello" session
|
|
||||||
looks rather inconvenient and takes multiple lines of code. For this reason, it
|
|
||||||
is a good practice to supply a convenience wrapper for creating sessions as
|
|
||||||
used for the timer session. This wrapper is also the right place to for
|
|
||||||
documenting session-construction arguments and assembling the argument string.
|
|
||||||
By convention, the wrapper is called 'connection.h' and placed in the directory
|
By convention, the wrapper is called 'connection.h' and placed in the directory
|
||||||
of the session interface. For our case, the file
|
of the session interface. For our case, the file
|
||||||
'include/hello_session/connection.h' looks like this:
|
'include/hello_session/connection.h' looks like this:
|
||||||
|
@ -372,27 +333,44 @@ of the session interface. For our case, the file
|
||||||
!#include <hello_session/client.h>
|
!#include <hello_session/client.h>
|
||||||
!#include <base/connection.h>
|
!#include <base/connection.h>
|
||||||
!
|
!
|
||||||
!namespace Hello {
|
!namespace Hello { struct Connection; }
|
||||||
!
|
!
|
||||||
! struct Connection : Genode::Connection<Session>, Session_client
|
!struct Hello::Connection : Genode::Connection<Session>, Session_client
|
||||||
! {
|
!{
|
||||||
! Connection()
|
! Connection(Genode::Env &env)
|
||||||
! :
|
! :
|
||||||
! /* create session */
|
! /* create session */
|
||||||
! Genode::Connection<Hello::Session>(session("foo, ram_quota=4K")),
|
! Genode::Connection<Hello::Session>(env, session(env.parent(),
|
||||||
|
! "ram_quota=4K")),
|
||||||
|
! /* initialize RPC interface */
|
||||||
|
! Session_client(cap()) { }
|
||||||
|
!};
|
||||||
|
|
||||||
|
|
||||||
|
Client implementation
|
||||||
|
=====================
|
||||||
|
|
||||||
|
The client-side implementation using the 'Hello::Connection' object is pretty
|
||||||
|
straightforward. Put this code into 'src/hello/client/main.cc':
|
||||||
|
|
||||||
|
!#include <base/component.h>
|
||||||
|
!#include <base/log.h>
|
||||||
|
!#include <hello_session/connection.h>
|
||||||
!
|
!
|
||||||
! /* initialize RPC interface */
|
!Genode::size_t Component::stack_size() { return 64*1024; }
|
||||||
! Session_client(cap()) { }
|
!
|
||||||
! };
|
!void Component::construct(Genode::Env &env)
|
||||||
|
!{
|
||||||
|
! Hello::Connection hello(env);
|
||||||
|
!
|
||||||
|
! hello.say_hello();
|
||||||
|
!
|
||||||
|
! int const sum = hello.add(2, 5);
|
||||||
|
! Genode::log("added 2 + 5 = ", sum);
|
||||||
|
!
|
||||||
|
! Genode::log("hello test completed");
|
||||||
!}
|
!}
|
||||||
|
|
||||||
With the 'Connection' class in place, we can now use Hello sessions
|
|
||||||
by just instantiating 'Hello::Connection' objects and invoke
|
|
||||||
functions directly on such an object. For example:
|
|
||||||
|
|
||||||
!Hello::Connection hello;
|
|
||||||
!int foo = hello.add(2, 5);
|
|
||||||
|
|
||||||
|
|
||||||
Ready, set, go...
|
Ready, set, go...
|
||||||
=================
|
=================
|
||||||
|
@ -403,19 +381,56 @@ Add a 'target.mk' file with the following content to 'src/hello/client/':
|
||||||
! SRC_CC = main.cc
|
! SRC_CC = main.cc
|
||||||
! LIBS = base
|
! LIBS = base
|
||||||
|
|
||||||
Extend your 'config' file as follows.
|
Extend your init _config_ as follows to also start the hello-client component:
|
||||||
|
|
||||||
# Add start entries for 'Timer' service and hello client:
|
! <start name="hello_client">
|
||||||
|
! <resource name="RAM" quantum="1M"/>
|
||||||
|
! </start>
|
||||||
|
|
||||||
! <start name="timer">
|
|
||||||
! <resource name="RAM" quantum="1M"/>
|
|
||||||
! <provides><service name="Timer"/></provides>
|
|
||||||
! </start>
|
|
||||||
! <start name="hello_client">
|
|
||||||
! <resource name="RAM" quantum="1M"/>
|
|
||||||
! </start>
|
|
||||||
|
|
||||||
Build 'drivers/timer', and 'hello/client', go to 'build/bin', and run './core'
|
Creating a run script to automate your work flow
|
||||||
again. You have now successfully implemented your first Genode client-server
|
================================================
|
||||||
scenario.
|
|
||||||
|
|
||||||
|
The procedure of building, configuring, integrating, and executing Genode
|
||||||
|
system scenarios across different kernels can be automated using a run
|
||||||
|
script, which can be executed directly from within your build directory.
|
||||||
|
A run script for the hello client-server scenario should be placed
|
||||||
|
at the _run/hello.run_ and look as follows:
|
||||||
|
|
||||||
|
!build { core init hello }
|
||||||
|
!
|
||||||
|
!create_boot_directory
|
||||||
|
!
|
||||||
|
!install_config {
|
||||||
|
!<config>
|
||||||
|
! <parent-provides>
|
||||||
|
! <service name="LOG"/>
|
||||||
|
! </parent-provides>
|
||||||
|
! <default-route>
|
||||||
|
! <any-service> <parent/> <any-child/> </any-service>
|
||||||
|
! </default-route>
|
||||||
|
! <start name="hello_server">
|
||||||
|
! <resource name="RAM" quantum="1M"/>
|
||||||
|
! <provides> <service name="Hello"/> </provides>
|
||||||
|
! </start>
|
||||||
|
! <start name="hello_client">
|
||||||
|
! <resource name="RAM" quantum="1M"/>
|
||||||
|
! </start>
|
||||||
|
!</config>}
|
||||||
|
!
|
||||||
|
!build_boot_image { core init hello_client hello_server }
|
||||||
|
!
|
||||||
|
!append qemu_args " -nographic "
|
||||||
|
!
|
||||||
|
!run_genode_until "hello test completed.*\n" 10
|
||||||
|
|
||||||
|
When executed via 'make run/hello', it performs the given steps in sequence.
|
||||||
|
Note that the run script is kernel-agnostic. Hence, you can execute the system
|
||||||
|
scenario on all the different kernels supported by Genode without any
|
||||||
|
modification. The regular expression specified to the 'run_genode_until' step
|
||||||
|
is used as pattern for detecting the success of the step. If the log output
|
||||||
|
produced by the scenario matches the pattern, the run script completes
|
||||||
|
successfully. If the pattern does not appear within the specified time (in
|
||||||
|
this example ten seconds), the run script aborts with an error. By creating
|
||||||
|
the run script, we have not just automated our work flow but have actually
|
||||||
|
created an automated test case for our components.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2013 Genode Labs GmbH
|
* Copyright (C) 2008-2016 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
@ -16,26 +16,27 @@
|
||||||
|
|
||||||
#include <hello_session/hello_session.h>
|
#include <hello_session/hello_session.h>
|
||||||
#include <base/rpc_client.h>
|
#include <base/rpc_client.h>
|
||||||
#include <base/printf.h>
|
#include <base/log.h>
|
||||||
|
|
||||||
namespace Hello {
|
namespace Hello { struct Session_client; }
|
||||||
|
|
||||||
struct Session_client : Genode::Rpc_client<Session>
|
|
||||||
|
struct Hello::Session_client : Genode::Rpc_client<Session>
|
||||||
|
{
|
||||||
|
Session_client(Genode::Capability<Session> cap)
|
||||||
|
: Genode::Rpc_client<Session>(cap) { }
|
||||||
|
|
||||||
|
void say_hello()
|
||||||
{
|
{
|
||||||
Session_client(Genode::Capability<Session> cap)
|
Genode::log("issue RPC for saying hello");
|
||||||
: Genode::Rpc_client<Session>(cap) { }
|
call<Rpc_say_hello>();
|
||||||
|
Genode::log("returned from 'say_hello' RPC call");
|
||||||
|
}
|
||||||
|
|
||||||
void say_hello()
|
int add(int a, int b)
|
||||||
{
|
{
|
||||||
PDBG("Saying Hello.");
|
return call<Rpc_add>(a, b);
|
||||||
call<Rpc_say_hello>();
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
int add(int a, int b)
|
|
||||||
{
|
|
||||||
return call<Rpc_add>(a, b);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _INCLUDE__HELLO_SESSION_H__CLIENT_H_ */
|
#endif /* _INCLUDE__HELLO_SESSION_H__CLIENT_H_ */
|
||||||
|
|
|
@ -17,18 +17,19 @@
|
||||||
#include <hello_session/client.h>
|
#include <hello_session/client.h>
|
||||||
#include <base/connection.h>
|
#include <base/connection.h>
|
||||||
|
|
||||||
namespace Hello {
|
namespace Hello { struct Connection; }
|
||||||
|
|
||||||
struct Connection : Genode::Connection<Session>, Session_client
|
|
||||||
{
|
|
||||||
Connection()
|
|
||||||
:
|
|
||||||
/* create session */
|
|
||||||
Genode::Connection<Hello::Session>(session("foo, ram_quota=4K")),
|
|
||||||
|
|
||||||
/* initialize RPC interface */
|
struct Hello::Connection : Genode::Connection<Session>, Session_client
|
||||||
Session_client(cap()) { }
|
{
|
||||||
};
|
Connection(Genode::Env &env)
|
||||||
}
|
:
|
||||||
|
/* create session */
|
||||||
|
Genode::Connection<Hello::Session>(env, session(env.parent(),
|
||||||
|
"ram_quota=4K")),
|
||||||
|
|
||||||
|
/* initialize RPC interface */
|
||||||
|
Session_client(cap()) { }
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _INCLUDE__HELLO_SESSION__CONNECTION_H_ */
|
#endif /* _INCLUDE__HELLO_SESSION__CONNECTION_H_ */
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2013 Genode Labs GmbH
|
* Copyright (C) 2008-2016 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
@ -17,25 +17,25 @@
|
||||||
#include <session/session.h>
|
#include <session/session.h>
|
||||||
#include <base/rpc.h>
|
#include <base/rpc.h>
|
||||||
|
|
||||||
namespace Hello {
|
namespace Hello { struct Session; }
|
||||||
|
|
||||||
struct Session : Genode::Session
|
|
||||||
{
|
|
||||||
static const char *service_name() { return "Hello"; }
|
|
||||||
|
|
||||||
virtual void say_hello() = 0;
|
|
||||||
virtual int add(int a, int b) = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************
|
struct Hello::Session : Genode::Session
|
||||||
** RPC interface **
|
{
|
||||||
*******************/
|
static const char *service_name() { return "Hello"; }
|
||||||
|
|
||||||
GENODE_RPC(Rpc_say_hello, void, say_hello);
|
virtual void say_hello() = 0;
|
||||||
GENODE_RPC(Rpc_add, int, add, int, int);
|
virtual int add(int a, int b) = 0;
|
||||||
|
|
||||||
GENODE_RPC_INTERFACE(Rpc_say_hello, Rpc_add);
|
|
||||||
};
|
/*******************
|
||||||
}
|
** RPC interface **
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
GENODE_RPC(Rpc_say_hello, void, say_hello);
|
||||||
|
GENODE_RPC(Rpc_add, int, add, int, int);
|
||||||
|
|
||||||
|
GENODE_RPC_INTERFACE(Rpc_say_hello, Rpc_add);
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _INCLUDE__HELLO_SESSION__HELLO_SESSION_H_ */
|
#endif /* _INCLUDE__HELLO_SESSION__HELLO_SESSION_H_ */
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Build
|
# Build
|
||||||
#
|
#
|
||||||
|
|
||||||
build { core init hello drivers/timer }
|
build { core init hello }
|
||||||
|
|
||||||
create_boot_directory
|
create_boot_directory
|
||||||
|
|
||||||
|
@ -14,27 +14,13 @@ install_config {
|
||||||
<config>
|
<config>
|
||||||
<parent-provides>
|
<parent-provides>
|
||||||
<service name="LOG"/>
|
<service name="LOG"/>
|
||||||
<service name="RM"/>
|
|
||||||
|
|
||||||
<!-- some timer implementations need kernel info pages -->
|
|
||||||
<service name="ROM"/>
|
|
||||||
|
|
||||||
<!-- hardware-based timers need I/O resources -->
|
|
||||||
<service name="IO_MEM"/>
|
|
||||||
<service name="IO_PORT"/>
|
|
||||||
<service name="IRQ"/>
|
|
||||||
</parent-provides>
|
</parent-provides>
|
||||||
<default-route>
|
<default-route>
|
||||||
<any-service> <parent/> <any-child/> </any-service>
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
</default-route>
|
</default-route>
|
||||||
|
|
||||||
<start name="hello_server">
|
<start name="hello_server">
|
||||||
<resource name="RAM" quantum="1M"/>
|
<resource name="RAM" quantum="1M"/>
|
||||||
<provides><service name="Hello"/></provides>
|
<provides> <service name="Hello"/> </provides>
|
||||||
</start>
|
|
||||||
<start name="timer">
|
|
||||||
<resource name="RAM" quantum="1M"/>
|
|
||||||
<provides><service name="Timer"/></provides>
|
|
||||||
</start>
|
</start>
|
||||||
<start name="hello_client">
|
<start name="hello_client">
|
||||||
<resource name="RAM" quantum="1M"/>
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
@ -45,8 +31,8 @@ install_config {
|
||||||
# Boot image
|
# Boot image
|
||||||
#
|
#
|
||||||
|
|
||||||
build_boot_image { core init hello_client hello_server timer }
|
build_boot_image { core init hello_client hello_server }
|
||||||
|
|
||||||
append qemu_args " -nographic "
|
append qemu_args " -nographic "
|
||||||
|
|
||||||
run_genode_until forever
|
run_genode_until "hello test completed.*\n" 10
|
||||||
|
|
|
@ -1,38 +1,33 @@
|
||||||
/*
|
/*
|
||||||
* \brief Test client for the Hello RPC interface
|
* \brief Test client for the Hello RPC interface
|
||||||
* \author Björn Döbel
|
* \author Björn Döbel
|
||||||
|
* \author Norman Feske
|
||||||
* \date 2008-03-20
|
* \date 2008-03-20
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2013 Genode Labs GmbH
|
* Copyright (C) 2008-2016 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <base/env.h>
|
#include <base/component.h>
|
||||||
#include <base/printf.h>
|
#include <base/log.h>
|
||||||
#include <hello_session/client.h>
|
|
||||||
#include <hello_session/connection.h>
|
#include <hello_session/connection.h>
|
||||||
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
|
|
||||||
using namespace Genode;
|
Genode::size_t Component::stack_size() { return 64*1024; }
|
||||||
|
|
||||||
int main(void)
|
|
||||||
|
void Component::construct(Genode::Env &env)
|
||||||
{
|
{
|
||||||
Hello::Connection h;
|
Hello::Connection hello(env);
|
||||||
|
|
||||||
Timer::Connection timer;
|
hello.say_hello();
|
||||||
|
|
||||||
while (1) {
|
int const sum = hello.add(2, 5);
|
||||||
h.say_hello();
|
Genode::log("added 2 + 5 = ", sum);
|
||||||
|
|
||||||
int foo = h.add(2, 5);
|
Genode::log("hello test completed");
|
||||||
PDBG("Added 2 + 5 = %d", foo);
|
|
||||||
timer.msleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,93 +1,91 @@
|
||||||
/*
|
/*
|
||||||
* \brief Main program of the Hello server
|
* \brief Main program of the Hello server
|
||||||
* \author Björn Döbel
|
* \author Björn Döbel
|
||||||
|
* \author Norman Feske
|
||||||
* \date 2008-03-20
|
* \date 2008-03-20
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2013 Genode Labs GmbH
|
* Copyright (C) 2008-2016 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <base/printf.h>
|
#include <base/component.h>
|
||||||
#include <base/env.h>
|
#include <base/log.h>
|
||||||
#include <base/sleep.h>
|
|
||||||
#include <cap_session/connection.h>
|
|
||||||
#include <root/component.h>
|
#include <root/component.h>
|
||||||
#include <hello_session/hello_session.h>
|
#include <hello_session/hello_session.h>
|
||||||
#include <base/rpc_server.h>
|
#include <base/rpc_server.h>
|
||||||
|
|
||||||
namespace Hello {
|
namespace Hello {
|
||||||
|
struct Session_component;
|
||||||
struct Session_component : Genode::Rpc_object<Session>
|
struct Root_component;
|
||||||
{
|
struct Main;
|
||||||
void say_hello() {
|
|
||||||
PDBG("I am here... Hello."); }
|
|
||||||
|
|
||||||
int add(int a, int b) {
|
|
||||||
return a + b; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class Root_component : public Genode::Root_component<Session_component>
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Hello::Session_component *_create_session(const char *args)
|
|
||||||
{
|
|
||||||
PDBG("creating hello session.");
|
|
||||||
return new (md_alloc()) Session_component();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Root_component(Genode::Rpc_entrypoint *ep,
|
|
||||||
Genode::Allocator *allocator)
|
|
||||||
: Genode::Root_component<Session_component>(ep, allocator)
|
|
||||||
{
|
|
||||||
PDBG("Creating root component.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
using namespace Genode;
|
struct Hello::Session_component : Genode::Rpc_object<Session>
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
{
|
||||||
/*
|
void say_hello() {
|
||||||
* Get a session for the parent's capability service, so that we
|
Genode::log("I am here... Hello."); }
|
||||||
* are able to create capabilities.
|
|
||||||
*/
|
int add(int a, int b) {
|
||||||
Cap_connection cap;
|
return a + b; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Hello::Root_component
|
||||||
|
:
|
||||||
|
public Genode::Root_component<Session_component>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
|
||||||
|
Session_component *_create_session(const char *args)
|
||||||
|
{
|
||||||
|
Genode::log("creating hello session");
|
||||||
|
return new (md_alloc()) Session_component();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Root_component(Genode::Entrypoint &ep,
|
||||||
|
Genode::Allocator &alloc)
|
||||||
|
:
|
||||||
|
Genode::Root_component<Session_component>(ep, alloc)
|
||||||
|
{
|
||||||
|
Genode::log("creating root component");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Hello::Main
|
||||||
|
{
|
||||||
|
Genode::Env &env;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A sliced heap is used for allocating session objects - thereby we
|
* A sliced heap is used for allocating session objects - thereby we
|
||||||
* can release objects separately.
|
* can release objects separately.
|
||||||
*/
|
*/
|
||||||
static Sliced_heap sliced_heap(env()->ram_session(),
|
Genode::Sliced_heap sliced_heap { env.ram(), env.rm() };
|
||||||
env()->rm_session());
|
|
||||||
|
|
||||||
/*
|
Hello::Root_component root { env.ep(), sliced_heap };
|
||||||
* Create objects for use by the framework.
|
|
||||||
*
|
|
||||||
* An 'Rpc_entrypoint' is created to announce our service's root
|
|
||||||
* capability to our parent, manage incoming session creation
|
|
||||||
* requests, and dispatch the session interface. The incoming RPC
|
|
||||||
* requests are dispatched via a dedicated thread. The 'STACK_SIZE'
|
|
||||||
* argument defines the size of the thread's stack. The additional
|
|
||||||
* string argument is the name of the entry point, used for
|
|
||||||
* debugging purposes only.
|
|
||||||
*/
|
|
||||||
enum { STACK_SIZE = 4096 };
|
|
||||||
static Rpc_entrypoint ep(&cap, STACK_SIZE, "hello_ep");
|
|
||||||
|
|
||||||
static Hello::Root_component hello_root(&ep, &sliced_heap);
|
Main(Genode::Env &env) : env(env)
|
||||||
env()->parent()->announce(ep.manage(&hello_root));
|
{
|
||||||
|
/*
|
||||||
|
* Create a RPC object capability for the root interface and
|
||||||
|
* announce the service to our parent.
|
||||||
|
*/
|
||||||
|
env.parent().announce(env.ep().manage(root));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* We are done with this and only act upon client requests now. */
|
|
||||||
sleep_forever();
|
|
||||||
|
|
||||||
return 0;
|
Genode::size_t Component::stack_size() { return 64*1024; }
|
||||||
|
|
||||||
|
|
||||||
|
void Component::construct(Genode::Env &env)
|
||||||
|
{
|
||||||
|
static Hello::Main main(env);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user